/*
* This file is part of NavTable
* Copyright (C) 2009 - 2010 Cartolab (Universidade da Coru�a)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* Authors:
* Juan Ignacio Varela Garc�a <nachouve (at) gmail (dot) com>
* Pablo Sanxiao Roca <psanxiao (at) gmail (dot) com>
* Javier Est�vez Vali�as <valdaris (at) gmail (dot) com>
* Jorge Lopez Fernandez <jlopez (at) cartolab (dot) es>
*/
package es.udc.cartolab.gvsig.navtable;
import java.io.IOException;
import java.text.ParseException;
import org.apache.log4j.Logger;
import com.hardcode.gdbms.driver.exceptions.InitializeWriterException;
import com.hardcode.gdbms.driver.exceptions.ReadDriverException;
import com.hardcode.gdbms.engine.data.driver.DriverException;
import com.hardcode.gdbms.engine.values.Value;
import com.hardcode.gdbms.engine.values.ValueFactory;
import com.iver.andami.PluginServices;
import com.iver.andami.messages.NotificationManager;
import com.iver.cit.gvsig.EditionUtilities;
import com.iver.cit.gvsig.ProjectExtension;
import com.iver.cit.gvsig.exceptions.expansionfile.ExpansionFileReadException;
import com.iver.cit.gvsig.exceptions.expansionfile.ExpansionFileWriteException;
import com.iver.cit.gvsig.exceptions.layers.CancelEditingLayerException;
import com.iver.cit.gvsig.exceptions.layers.StartEditionLayerException;
import com.iver.cit.gvsig.exceptions.table.CancelEditingTableException;
import com.iver.cit.gvsig.exceptions.validate.ValidateRowException;
import com.iver.cit.gvsig.exceptions.visitors.StartWriterVisitorException;
import com.iver.cit.gvsig.exceptions.visitors.StopWriterVisitorException;
import com.iver.cit.gvsig.fmap.core.DefaultFeature;
import com.iver.cit.gvsig.fmap.core.DefaultRow;
import com.iver.cit.gvsig.fmap.core.FShape;
import com.iver.cit.gvsig.fmap.core.IFeature;
import com.iver.cit.gvsig.fmap.core.IGeometry;
import com.iver.cit.gvsig.fmap.core.IRow;
import com.iver.cit.gvsig.fmap.drivers.FieldDescription;
import com.iver.cit.gvsig.fmap.drivers.ILayerDefinition;
import com.iver.cit.gvsig.fmap.drivers.ITableDefinition;
import com.iver.cit.gvsig.fmap.edition.EditionEvent;
import com.iver.cit.gvsig.fmap.edition.EditionExceptionOld;
import com.iver.cit.gvsig.fmap.edition.IEditableSource;
import com.iver.cit.gvsig.fmap.edition.IRowEdited;
import com.iver.cit.gvsig.fmap.edition.ISpatialWriter;
import com.iver.cit.gvsig.fmap.edition.IWriteable;
import com.iver.cit.gvsig.fmap.edition.IWriter;
import com.iver.cit.gvsig.fmap.edition.VectorialEditableAdapter;
import com.iver.cit.gvsig.fmap.edition.rules.IRule;
import com.iver.cit.gvsig.fmap.edition.rules.RulePolygon;
import com.iver.cit.gvsig.fmap.layers.FLayer;
import com.iver.cit.gvsig.fmap.layers.FLyrVect;
import com.iver.cit.gvsig.project.documents.table.ProjectTable;
import com.iver.cit.gvsig.project.documents.table.gui.Table;
import es.udc.cartolab.gvsig.navtable.format.ValueFactoryNT;
/**
* Class for start, stop or toggle the editing on a vector layer. Based on the
* StartingEditing Extension of gvSIG
*
* @author Nacho Varela
* @author Javier Estevez
* @author Pablo Sanxiao
* @author Francisco Puga
* @author Andres Maneiro
* @author Jorge Lopez
*/
public class ToggleEditing {
protected static Logger logger = Logger.getLogger("ToggleEditing");
/**
* @param layer - The vectorial layer to be edited.
*/
public boolean startEditing(FLayer layer) {
if (layer instanceof FLyrVect) {
layer.setActive(true);
FLyrVect lv = (FLyrVect) layer;
try {
lv.setEditing(true);
} catch (StartEditionLayerException e) {
logger.error(e.getMessage(), e);
}
VectorialEditableAdapter vea = (VectorialEditableAdapter) lv
.getSource();
vea.getRules().clear();
try {
if (vea.getShapeType() == FShape.POLYGON) {
IRule rulePol = new RulePolygon();
vea.getRules().add(rulePol);
}
} catch (ReadDriverException e) {
logger.error(e.getMessage(), e);
return false;
}
// If there's a table linked to this layer, its model is changed
// to VectorialEditableAdapter.
ProjectExtension pe = (ProjectExtension) PluginServices
.getExtension(ProjectExtension.class);
if (pe != null) {
ProjectTable pt = pe.getProject().getTable(lv);
if (pt != null) {
pt.setModel(vea);
Table table = getModelTable(pt);
if(table != null) {
table.setModel(pt);
vea.getCommandRecord().addCommandListener(table);
}
}
}
}
return true;
}
public boolean startEditing(IEditableSource source) {
try {
source.startEdition(EditionEvent.ALPHANUMERIC);
return true;
} catch (StartWriterVisitorException e) {
logger.error(e.getMessage(), e);
return false;
}
}
/**
* @param layer
* - The layer wich edition will be stoped.
* @param cancel
* - false if we want to save the layer, true if we don't.
* @throws StopWriterVisitorException
*/
public boolean stopEditing(FLayer layer, boolean cancel)
throws StopWriterVisitorException {
if (layer instanceof FLyrVect) {
try {
if (cancel) {
cancelEdition(layer);
} else {
saveLayer((FLyrVect) layer);
}
} catch (DriverException e) {
logger.error(e.getMessage(), e);
return false;
} catch (EditionExceptionOld e) {
logger.error(e.getMessage(), e);
return false;
} finally {
try {
layer.setActive(true);
layer.setEditing(false);
} catch (StartEditionLayerException e) {
logger.error(e.getMessage(), e);
return false;
}
}
return true;
}
return false;
}
private void cancelEdition(FLayer layer) {
layer.setProperty("stoppingEditing", new Boolean(true));
VectorialEditableAdapter vea = (VectorialEditableAdapter)
((FLyrVect) layer).getSource();
try {
vea.cancelEdition(EditionEvent.GRAPHIC);
} catch (CancelEditingLayerException e) {
logger.error(e.getMessage(), e);
}
Table table = getTableFromLayer(layer);
if(table != null){
try {
table.cancelEditing();
} catch (CancelEditingTableException e) {
logger.error(e.getMessage(), e);
}
}
layer.setProperty("stoppingEditing", new Boolean(false));
}
public boolean stopEditing(IEditableSource source) {
try {
IWriteable w = (IWriteable) source;
IWriter writer = w.getWriter();
if (writer == null) {
NotificationManager.addError(
"No existe driver de escritura para la tabla"
+ source.getRecordset().getName(),
new EditionExceptionOld());
return false;
} else {
ITableDefinition tableDef = source.getTableDefinition();
writer.initialize(tableDef);
source.stopEdition(writer, EditionEvent.ALPHANUMERIC);
return true;
}
} catch (ReadDriverException e) {
logger.error(e.getMessage(), e);
return false;
} catch (InitializeWriterException e) {
logger.error(e.getMessage(), e);
return false;
} catch (StopWriterVisitorException e) {
logger.error(e.getMessage(), e);
return false;
}
}
private void saveLayer(FLyrVect layer) throws DriverException,
EditionExceptionOld, StopWriterVisitorException {
layer.setProperty("stoppingEditing", new Boolean(true));
VectorialEditableAdapter vea = (VectorialEditableAdapter) layer
.getSource();
ISpatialWriter writer = (ISpatialWriter) vea.getWriter();
Table table = getTableFromLayer(layer);
if(table != null){
table.stopEditingCell();
}
vea.cleanSelectableDatasource();
try {
layer.setRecordset(vea.getRecordset());
} catch (ReadDriverException e) {
logger.error(e.getMessage(), e);
}
// The layer recordset must have the changes we made
ILayerDefinition lyrDef;
try {
lyrDef = EditionUtilities.createLayerDefinition(layer);
String aux = "FIELDS:";
FieldDescription[] flds = lyrDef.getFieldsDesc();
for (int i = 0; i < flds.length; i++) {
aux = aux + ", " + flds[i].getFieldAlias();
}
lyrDef.setShapeType(layer.getShapeType());
writer.initialize(lyrDef);
vea.stopEdition(writer, EditionEvent.GRAPHIC);
layer.setProperty("stoppingEditing", new Boolean(false));
} catch (ReadDriverException e) {
logger.error(e.getMessage(), e);
} catch (InitializeWriterException e) {
logger.error(e.getMessage(), e);
}
}
/**
* Modify a single value of a register. It creates the new value from its
* String representation. IMPORTANT: StartEditing and StopEditing is
* required before and after call this method.
*
* @param layer
* the layer that contains the feature to be changed.
* @param rowPosition
* the data row to be changed.
* @param colPosition
* the data column position to be changed.
* @param newValue
* the String representation of the value to be saved.
*
*/
public void modifyValue(FLyrVect layer, int rowPosition, int colPosition,
String newValue) throws Exception {
IEditableSource source = (IEditableSource) layer.getSource();
Value value = getNewAttribute(source, colPosition, newValue);
modifyValue(layer, rowPosition, colPosition, value);
}
/**
* Modidify a single value of a register. IMPORTANT: StartEditing and
* StopEditing is required before and after call this method.
*
* @param layer
* the layer that contains the feature to be changed.
* @param rowPosition
* the data row to be changed.
* @param colPosition
* the data column position to be changed.
* @param newValue
* the value to be saved.
*
* @throws DriverException
* @throws IOException
*
*/
public void modifyValue(FLyrVect layer, int rowPosition, int colPosition,
Value newValue) throws Exception {
IEditableSource source = (IEditableSource) layer.getSource();
IRowEdited row = source.getRow(rowPosition);
Value[] attributes = row.getAttributes();
if (row.getLinkedRow() instanceof IFeature) {
attributes[colPosition] = newValue;
IGeometry geometry = getTheGeom(source, rowPosition);
IRow newRow = new DefaultFeature(geometry, attributes,
row.getID());
source.modifyRow(rowPosition, newRow, "NAVTABLE MODIFY",
EditionEvent.ALPHANUMERIC);
} else {
logger.error("Row has no geometry");
}
}
public void modifyValue(IEditableSource source, int rowPosition, int colPosition,
String newValue) throws Exception {
IRowEdited row = source.getRow(rowPosition);
Value[] attributes = row.getAttributes();
attributes[colPosition] = getNewAttribute(source, colPosition, newValue);
IRow newRow = new DefaultRow(attributes);
source.modifyRow(rowPosition, newRow, "NAVTABLE MODIFY",
EditionEvent.ALPHANUMERIC);
}
/**
* Modify an the desired of values of a register IMPORTANT: StartEditing and
* StopEditing is required before and after call this method.
*
* @param layer
* the layer that contains the feature to be changed.
* @param rowPosition
* the data row to be changed.
* @param attIndexes
* An array that contains the index of the columns that will be
* modified.
* @param attValues
* the new values to save (in correlative order with attPos)
*
* @throws DriverException
* @throws IOException
*
*/
public void modifyValues(FLyrVect layer,
int rowPosition,
int[] attIndexes,
String[] attValues) {
try {
IEditableSource source = (IEditableSource) layer.getSource();
IGeometry geometry = getTheGeom(source, rowPosition);
Value[] values = getNewAttributes(source, rowPosition, attIndexes, attValues);
IRowEdited row = source.getRow(rowPosition);
IRow newRow = new DefaultFeature(geometry, values, row.getID());
source.modifyRow(rowPosition, newRow, "NAVTABLE MODIFY",
EditionEvent.ALPHANUMERIC);
} catch (ExpansionFileWriteException e) {
logger.error(e.getMessage(), e);
} catch (ExpansionFileReadException e) {
logger.error(e.getMessage(), e);
} catch (ValidateRowException e) {
logger.error(e.getMessage(), e);
} catch (ReadDriverException e) {
logger.error(e.getMessage(), e);
}
}
public void modifyValues(IEditableSource source, int rowPosition,
int[] attIndexes, String[] attValues) {
try {
Value[] attributes = getNewAttributes(
source, rowPosition, attIndexes, attValues);
IRow newRow = new DefaultRow(attributes);
source.modifyRow(rowPosition, newRow, "NAVTABLE MODIFY",
EditionEvent.ALPHANUMERIC);
} catch (ExpansionFileReadException e) {
logger.error(e.getMessage(), e);
} catch (ReadDriverException e) {
logger.error(e.getMessage(), e);
} catch (ValidateRowException e) {
logger.error(e.getMessage(), e);
}
}
public void deleteRow(FLyrVect layer, int position) {
try {
IEditableSource source = (IEditableSource) layer.getSource();
source.removeRow(position, "NAVTABLE DELETE", EditionEvent.ALPHANUMERIC);
} catch (ExpansionFileReadException e) {
e.printStackTrace();
} catch (ReadDriverException e) {
e.printStackTrace();
}
}
private Value[] getNewAttributes(IEditableSource source,
int rowPosition,
int[] attIndexes,
String[] attValues) {
int type;
try {
FieldDescription[] fieldDesc = source.getTableDefinition().getFieldsDesc();
Value[] attributes = source.getRow(rowPosition).getAttributes();
for (int i = 0; i < attIndexes.length; i++) {
String att = attValues[i];
int idx = attIndexes[i];
if (att == null || att.length() == 0) {
attributes[idx] = ValueFactoryNT.createNullValue();
} else {
type = fieldDesc[idx].getFieldType();
try {
attributes[idx] = ValueFactoryNT.createValueByType(att, type);
} catch (ParseException e) {
logger.warn(e.getStackTrace(), e);
}
}
}
return attributes;
} catch (ReadDriverException e) {
logger.error(e.getMessage(), e);
return null;
}
}
private Value getNewAttribute(IEditableSource source, int colPosition, String newValue) {
if (newValue == null) {
newValue = "";
}
try {
ITableDefinition tableDef = source.getTableDefinition();
FieldDescription[] fieldDesc = tableDef.getFieldsDesc();
int type = fieldDesc[colPosition].getFieldType();
Value val;
if (newValue.length() == 0) {
val = ValueFactoryNT.createNullValue();
} else {
val = ValueFactoryNT.createValueByType(newValue, type);
}
return val;
} catch (ReadDriverException e) {
logger.error(e.getStackTrace(), e);
return null;
} catch (ParseException e) {
logger.warn(e.getStackTrace(), e);
return null;
}
}
private IGeometry getTheGeom(IEditableSource source, int rowPosition) {
try {
IRowEdited row = source.getRow(rowPosition);
return ((DefaultFeature) row.getLinkedRow()).getGeometry();
} catch (Exception e) {
logger.error(e.getMessage(), e);
return null;
}
}
private Table getModelTable(ProjectTable pt) {
// TODO: see how drop this IWindow dependence, by getting the Table
// from internal info (layer, MapControl) instead of iterating
// through all windows
com.iver.andami.ui.mdiManager.IWindow[] views = PluginServices
.getMDIManager().getAllWindows();
for (int i = 0; i < views.length; i++) {
if (views[i] instanceof Table) {
Table table = (Table) views[i];
ProjectTable model = table.getModel();
if (model.equals(pt)) {
return table;
}
}
}
return null;
}
private Table getTableFromLayer(FLayer layer) {
//TODO: see how drop this IWindow dependence
com.iver.andami.ui.mdiManager.IWindow[] views = null;
try {
views = PluginServices.getMDIManager().getAllWindows();
for (int j = 0; j < views.length; j++) {
if (views[j] instanceof Table) {
Table table = (Table) views[j];
if (table.getModel().getAssociatedTable() != null
&& table.getModel().getAssociatedTable().equals(layer)) {
return table;
}
}
}
} catch (NullPointerException e) {}
return null;
}
}