Index: CSVStoreProvider.java
===================================================================
--- CSVStoreProvider.java	(revision 44407)
+++ CSVStoreProvider.java	(revision 44408)
@@ -26,7 +26,6 @@
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileReader;
-import java.io.FileWriter;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.net.URI;
@@ -37,7 +36,7 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
-import java.util.logging.Level;
+import java.util.Map;
 
 import org.apache.commons.io.FilenameUtils;
 import org.apache.commons.io.IOUtils;
@@ -50,7 +49,6 @@
 import org.gvsig.fmap.dal.DataStoreNotification;
 import org.gvsig.fmap.dal.DataTypes;
 import org.gvsig.fmap.dal.FileHelper;
-import org.gvsig.fmap.dal.exception.CloseException;
 import org.gvsig.fmap.dal.exception.DataException;
 import org.gvsig.fmap.dal.exception.InitializeException;
 import org.gvsig.fmap.dal.exception.OpenException;
@@ -82,17 +80,18 @@
 import org.gvsig.fmap.geom.Geometry;
 import org.gvsig.fmap.geom.GeometryLocator;
 import org.gvsig.fmap.geom.GeometryManager;
+import org.gvsig.fmap.geom.GeometryUtils;
 import org.gvsig.fmap.geom.aggregate.MultiPoint;
 import org.gvsig.fmap.geom.primitive.Envelope;
 import org.gvsig.fmap.geom.primitive.Point;
 import org.gvsig.fmap.geom.type.GeometryType;
 import org.gvsig.tools.ToolsLocator;
-import org.gvsig.tools.dataTypes.CoercionException;
 import org.gvsig.tools.dataTypes.DataType;
 import org.gvsig.tools.dataTypes.DataTypesManager;
 import org.gvsig.tools.dataTypes.DataTypesManager.Coercion;
 import org.gvsig.tools.dataTypes.DataTypesManager.CoercionWithLocale;
 import org.gvsig.tools.dispose.DisposableIterator;
+import org.gvsig.tools.dynobject.Tags;
 import org.gvsig.tools.dynobject.exception.DynFieldNotFoundException;
 import org.gvsig.tools.evaluator.AbstractEvaluator;
 import org.gvsig.tools.evaluator.EvaluatorData;
@@ -107,14 +106,13 @@
 import org.gvsig.tools.visitor.Visitor;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.supercsv.io.CsvListWriter;
 import org.supercsv.prefs.CsvPreference;
 
 @SuppressWarnings("UseSpecificCatch")
 public class CSVStoreProvider extends AbstractMemoryStoreProvider implements
         ResourceConsumer {
 
-    private static final Logger logger = LoggerFactory.getLogger(CSVStoreProvider.class);
+    private static final Logger LOGGER = LoggerFactory.getLogger(CSVStoreProvider.class);
 
     public static final String NAME = "CSV";
     public static final String DESCRIPTION = "CSV file";
@@ -189,10 +187,10 @@
         try {
             loadFeatures();
         } catch (RuntimeException e) {
-            logger.debug("Can't load features from CSV '" + getFullFileName() + "'.", e);
+            LOGGER.debug("Can't load features from CSV '" + getFullFileName() + "'.", e);
             throw e;
         } catch (Exception e) {
-            logger.debug("Can't load features from CSV '" + getFullFileName() + "'.", e);
+            LOGGER.debug("Can't load features from CSV '" + getFullFileName() + "'.", e);
             throw new RuntimeException(e);
         }
     }
@@ -245,7 +243,7 @@
                                     (org.gvsig.fmap.dal.feature.Feature) it.next());
                             writer.add(feature);
                             if (feature.getOID() == null) {
-                                logger.warn("feature without OID");
+                                LOGGER.warn("feature without OID");
                                 feature.setOID(createNewOID());
                             }
                             newdata.add(feature);
@@ -341,7 +339,7 @@
             });
             writer.end();
         } catch (PerformEditingException ex) {
-            logger.warn("Not been able to end append '" + this.getFullName() + "'.", ex);
+            LOGGER.warn("Not been able to end append '" + this.getFullName() + "'.", ex);
         }
     }
 
@@ -392,7 +390,7 @@
                 }
             });
         } catch (BaseException e) {
-            logger.warn("Can't calculate the envelope of CSV file '" + this.getFullName() + "'.", e);
+            LOGGER.warn("Can't calculate the envelope of CSV file '" + this.getFullName() + "'.", e);
             this.envelope = null;
         }
 
@@ -457,120 +455,148 @@
     private void init(CSVStoreParameters parameters, DataStoreProviderServices storeServices) {
         throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
     }
-
     private class FieldTypeParser {
 
         public String name = null;
         public int type = DataTypes.STRING;
         public int size = 0;
-        public boolean allowNulls = true;
-        public int geometryType = Geometry.TYPES.GEOMETRY;
+        public int geomType = Geometry.TYPES.GEOMETRY;
+        public int geomSubtype = Geometry.SUBTYPES.GEOM2D;
+        public Map<String,String> tags = new HashMap<>();
+        public Map<String,String> assignments = new HashMap<>();
 
         private String typename = "string";
 
         FieldTypeParser() {
         }
 
+        public void clear() {
+            this.name = null;
+            this.type = DataTypes.STRING;
+            this.size = 0;
+            this.tags = new HashMap<>();
+            this.assignments = new HashMap<>();
+        }
+
+        public void copyFrom(FieldTypeParser other) {
+            this.name = other.name;
+            this.type = other.type;
+            this.size = other.size;
+            this.tags = new HashMap<>();
+            this.tags.putAll(other.tags);
+            this.assignments = new HashMap<>();
+            this.assignments.putAll(other.assignments);
+        }
+
         private int getType(String value) {
             DataTypesManager dataTypesManager = ToolsLocator.getDataTypesManager();
             return dataTypesManager.getType(typename);
         }
 
-        public void clear() {
-            name = null;
-            type = DataTypes.STRING;
-            size = 0;
-            allowNulls = true;
-            geometryType = Geometry.TYPES.GEOMETRY;
+        private String[] split(String value, String separators) {
+            int firstSeparatorPosition = 1000000;
+            Character sep = null;
+            for (char ch : separators.toCharArray()) {
+                int pos = value.indexOf(ch);
+                if( pos>0 && pos<firstSeparatorPosition ) {
+                    sep = ch;
+                    firstSeparatorPosition = pos;
+                }
+            }
+            if( sep == null ) {
+                return new String[] { value };
+            }
+            return value.split("["+sep+"]");
         }
 
-        public void copyFrom(FieldTypeParser other) {
-            name = other.name;
-            type = other.type;
-            size = other.size;
-            allowNulls = other.allowNulls;
-            geometryType = other.geometryType;
-        }
-
-        // El formato seria:
-        //   name[:typename[:size[:notnull|null]]]
-        //   name[:GEOMETRY[:geometry_type[:notnull|null]]]
-        //   name[__typename[__size[__notnull|null]]]
-        //
+        @SuppressWarnings("UseSpecificCatch")
         public boolean parse(String value) {
-            String theTypename = null;
-            String[] ss = null;
-            if (value.contains(":")) {
-                ss = value.split(":");
-            } else if (value.contains("__")) {
-                ss = value.split("__");
-            }
-            if (ss == null) {
-                this.name = value;
+            String[] args;
+            if ( value.contains("__") ) {
+                args = value.split("__");
+            } else {
+                args = split(value, ":/#@!;-");
+                if( args.length == 1 ) {
+                    this.name = value;
+                    return true;
+                }
+            }                        
+            int n = 0;
+            this.name = args[n++];
+            if( n >= args.length ) {
                 return true;
             }
-            switch (ss.length) {
-                case 4:
-                    if (!StringUtils.isBlank(ss[3])) {
-                        if ("notnull".equalsIgnoreCase(ss[3].trim())) {
-                            this.allowNulls = false;
-                        } else {
-                            this.allowNulls = true;
+            this.typename = args[n++];
+            this.type = this.getType(this.typename);
+            if ( this.type == DataTypes.INVALID ) {
+                this.geomType = GeometryUtils.getGeometryType(this.typename);
+                if( this.geomType==Geometry.TYPES.UNKNOWN )  {
+                    this.type = DataTypes.STRING;
+                    LOGGER.info("Type '" + this.typename + "' not valid for attribute '" + value + "' in '"+getProviderName()+"' file '" + getFullFileName() + "'.");
+                } else {
+                    this.typename = "GEOMETRY";
+                    this.type = DataTypes.GEOMETRY;
+                }
+            }
+            switch(this.type) {
+                case DataTypes.STRING:
+                    this.size = 50;
+                    break;
+                case DataTypes.INT:
+                    this.size = 10;
+                    break;
+                case DataTypes.LONG:
+                    this.size = 20;
+                    break;
+                case DataTypes.FLOAT:
+                    this.size = 10;
+                    break;
+                case DataTypes.DOUBLE:
+                    this.size = 20;
+                    break;
+                default:
+                    this.size = 0;
+            }
+            while (n < args.length) {
+                String option = args[n++].toLowerCase();
+                switch (option) {
+                    case "size":
+                        try {
+                            this.size = Integer.parseInt(args[n++]);
+                        } catch (Exception ex) {
+                            LOGGER.warn("Ignore incorrect field size for field " + value + " in '"+getProviderName()+"' file '" + getFullFileName() + "'.", ex);
                         }
-                    }
-                case 3:
-                    if (!StringUtils.isBlank(ss[1])) {
-                        this.typename = ss[1].trim();
-                        this.type = this.getType(this.typename);
-                        if (this.type == DataTypes.INVALID) {
-                            this.type = DataTypes.STRING;
-                            logger.info("Type '" + theTypename + "' not valid for attribute '" + value + "' in CSV file '" + getFullFileName() + "'.");
+                        break;
+                    case "tag": {
+                            String x = args[n++];
+                            int pos = x.indexOf("=");
+                            if( pos < 0 ) {
+                                this.tags.put(x, null);
+                            } else {
+                                this.tags.put( 
+                                        StringUtils.substring(x, 0, pos),
+                                        StringUtils.substring(x, pos+1)
+                                );
+                            }
+                            break;
                         }
-                    }
-                    if (!StringUtils.isBlank(ss[2])) {
-                        if (this.type == DataTypes.GEOMETRY) {
-                            String s = ss[2].trim();
-                            if (s.equalsIgnoreCase("line") || s.equalsIgnoreCase("linestring") || s.equalsIgnoreCase("curve")) {
-                                this.geometryType = Geometry.TYPES.LINE;
-                            } else if (s.equalsIgnoreCase("multiline") || s.equalsIgnoreCase("multilinestring") || s.equalsIgnoreCase("multicurve")) {
-                                this.geometryType = Geometry.TYPES.MULTILINE;
-                            } else if (s.equalsIgnoreCase("point")) {
-                                this.geometryType = Geometry.TYPES.POINT;
-                            } else if (s.equalsIgnoreCase("multipoint")) {
-                                this.geometryType = Geometry.TYPES.MULTIPOINT;
-                            } else if (s.equalsIgnoreCase("polygon") || s.equalsIgnoreCase("surface")) {
-                                this.geometryType = Geometry.TYPES.POLYGON;
-                            } else if (s.equalsIgnoreCase("multipolygon") || s.equalsIgnoreCase("multisurface")) {
-                                this.geometryType = Geometry.TYPES.MULTIPOLYGON;
+                    case "set": {
+                            String x = args[n++];
+                            int pos = x.indexOf("=");
+                            if( pos < 0 ) {
+                                this.assignments.put(x, null);
+                            } else {
+                                this.assignments.put( 
+                                        StringUtils.substring(x, 0, pos),
+                                        StringUtils.substring(x, pos+1)
+                                );
                             }
-                            this.size = 1;
-                        } else {
-                            try {
-                                this.size = Integer.parseInt(ss[2]);
-                            } catch (Exception ex) {
-                                logger.warn("Ignore incorrect field size for field " + value + " in CSV header of '" + getFullFileName() + "'.", ex);
-                            }
+                            break;
                         }
-                    }
-                    this.name = ss[0].trim();
-                    break;
-                case 2:
-                    if (!StringUtils.isBlank(ss[1])) {
-                        this.typename = ss[1].trim();
-                        this.type = this.getType(this.typename);
-                        if (this.type == DataTypes.INVALID) {
-                            this.type = DataTypes.STRING;
-                            logger.info("Type '" + theTypename + "' not valid for attribute '" + value + "' in CSV file '" + getFullFileName() + "'.");
-                        }
-                    }
-                case 1:
-                    this.name = ss[0].trim();
-                    break;
+                    default:
+                        LOGGER.warn("Illegal argumente '"+option+"' for field '"+this.name+"' in '"+getProviderName()+"' file '" + getFullFileName() + "' ("+value+").");
+                }
             }
-
-            if (this.type != DataTypes.STRING) {
-                this.size = 0;
-            }
             return true;
         }
 
@@ -627,39 +653,68 @@
         //
         // Una vez ya sabemos los tipos de datos rellenamos el feature-type
         //
-        for (int i = 0; i < fieldTypes.length; i++) {
-            EditableFeatureAttributeDescriptor fad = fType.add(
-                    fieldTypes[i].name,
-                    fieldTypes[i].type
-            );
-            fad.setSize(fieldTypes[i].size);
-            fad.setAllowNull(fieldTypes[i].allowNulls);
-            if (fieldTypes[i].type == DataTypes.GEOMETRY) {
-                if (fType.getDefaultGeometryAttributeName() == null) {
-                    fType.setDefaultGeometryAttributeName(fieldTypes[i].name);
+        for (FieldTypeParser fieldType : fieldTypes) {
+            EditableFeatureAttributeDescriptor fad = fType.add(fieldType.name, fieldType.type);
+            fad.setSize(fieldType.size);
+            if (fieldType.type == DataTypes.GEOMETRY ) {
+                fad.setGeometryType(fieldType.geomType, fieldType.geomSubtype);
+                if( fType.getDefaultGeometryAttributeName() == null ) {
+                    fType.setDefaultGeometryAttributeName(fieldType.name);
                 }
-                fad.setGeometryType(fieldTypes[i].geometryType);
             }
+            for (Map.Entry<String, String> entry : fieldType.assignments.entrySet()) {
+                try {
+                    switch(entry.getKey().toLowerCase()) {
+                        case "expression":
+                            // Los campos calculados los procesamos en una segunda
+                            // pasada, cuando ya estan definidos el resto de los campos
+                            // ya que pueden requerir campos que aun no se han definido.
+                            break;
+                        default:
+                            fad.set(entry.getKey(), entry.getValue());
+                    }
+                } catch (Exception ex) {
+                    LOGGER.warn("Can't set property '"+entry.getKey()+"' of '"+fad.getName()+"'.", ex);
+                }
+            }
+            Tags tags = fad.getTags();
+            for (Map.Entry<String, String> entry : fieldType.tags.entrySet()) {
+                tags.set(entry.getKey(), entry.getValue());
+            }
         }
+        // Processamos ahora los campos calculados
+        for (FieldTypeParser fieldType : fieldTypes) {
+            EditableFeatureAttributeDescriptor fad = fType.getEditableAttributeDescriptor(fieldType.name);
+            for (Map.Entry<String, String> entry : fieldType.assignments.entrySet()) {
+                try {
+                    switch(entry.getKey().toLowerCase()) {
+                        case "expression":
+                            fad.set(entry.getKey(), entry.getValue());
+                            break;
+                    }
+                } catch (Exception ex) {
+                    LOGGER.warn("Can't set property '"+entry.getKey()+"' in '"+fad.getName()+"' of '"+getFullFileName()+"'.", ex);
+                }
+            }
+        }
         String[] pointDimensionNames = CSVStoreParameters.getPointDimensionNames(this.getParameters());
-        if (pointDimensionNames != null) {
+        if ( pointDimensionNames != null ) {
             PointAttributeEmulator emulator = new PointAttributeEmulator(pointDimensionNames);
-            EditableFeatureAttributeDescriptor attr = fType.add(
-                    CSVStoreParameters.getPointColumnName(this.getParameters()),
-                    DataTypes.GEOMETRY, emulator
-            );
+            EditableFeatureAttributeDescriptor attr = fType.add("the_geom", DataTypes.GEOMETRY, emulator);
             GeometryManager geommgr = GeometryLocator.getGeometryManager();
             GeometryType gt;
             try {
-                gt = geommgr.getGeometryType(Geometry.TYPES.GEOMETRY, Geometry.SUBTYPES.GEOM3D);
+                if ( emulator.fieldNames != null && emulator.fieldNames.length <= 2 ) {
+                	gt = geommgr.getGeometryType(Geometry.TYPES.GEOMETRY, Geometry.SUBTYPES.GEOM2D);
+                } else {
+                	gt = geommgr.getGeometryType(Geometry.TYPES.GEOMETRY, Geometry.SUBTYPES.GEOM3D);
+                }
                 attr.setGeometryType(gt);
-                attr.setGeometryType(Geometry.TYPES.POINT);
-                attr.setGeometrySubType(Geometry.SUBTYPES.GEOM3D);
             } catch (Exception e) {
-                logger.warn("Can't set geometry type for the calculated field in CSV file '" + getFullFileName() + "'.", e);
+                LOGGER.warn("Can't set geometry type for the calculated field in '"+getProviderName()+"' file '" + getFullFileName() + "'.", e);
             }
-        }
-
+        }        
+        
         String geometry_column = CSVStoreParameters.getGeometryColumn(this.getParameters());
         if (!StringUtils.isEmpty(geometry_column)) {
             EditableFeatureAttributeDescriptor attr = (EditableFeatureAttributeDescriptor) fType.get(geometry_column);
@@ -671,7 +726,7 @@
                     gt = geommgr.getGeometryType(Geometry.TYPES.GEOMETRY, Geometry.SUBTYPES.UNKNOWN);
                     attr.setGeometryType(gt);
                 } catch (Exception e) {
-                    logger.warn("Can't set geometry type for the calculated field in CSV file '" + getFullFileName() + "'.", e);
+                    LOGGER.warn("Can't set geometry type for the calculated field in CSV file '" + getFullFileName() + "'.", e);
                 }
                 fType.setDefaultGeometryAttributeName(geometry_column);
             }
@@ -860,10 +915,10 @@
                 try {
                     charset = Charset.forName(charsetName);
                 } catch (Throwable th) {
-                    logger.warn("Can't use charset '" + charsetName + "' for read csv '" + this.getFullFileName() + "'.", th);
+                    LOGGER.warn("Can't use charset '" + charsetName + "' for read csv '" + this.getFullFileName() + "'.", th);
                 }
             } else {
-                logger.warn("charset '" + charsetName + "' not supported for read csv '" + this.getFullFileName() + "'.");
+                LOGGER.warn("charset '" + charsetName + "' not supported for read csv '" + this.getFullFileName() + "'.");
             }
         }
         InputStreamReader isr = new InputStreamReader(fis, charset);
@@ -898,7 +953,7 @@
                                     + this.getCSVParameters().getFile()
                                             .getAbsolutePath()
                                     + "' and not specified in the parameters.";
-                            logger.warn(msg);
+                            LOGGER.warn(msg);
                             throw new RuntimeException(msg);
                         }
                     }
@@ -1006,10 +1061,10 @@
                             throw ex;
                         }
                         if (count_errors++ < 10) {
-                            logger.warn("Can't load value of attribute " + i + " in row " + count + ".", ex);
+                            LOGGER.warn("Can't load value of attribute " + i + " in row " + count + ".", ex);
                         }
                         if (count_errors == 10) {
-                            logger.info("Too many errors, suppress messages.");
+                            LOGGER.info("Too many errors, suppress messages.");
                         }
                     }
                 }
