package org.gcube.application.cms.sdi.engine; import lombok.*; import lombok.extern.slf4j.Slf4j; import org.gcube.application.cms.sdi.faults.DataParsingException; import org.gcube.application.geoportal.common.model.legacy.BBOX; import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Types; import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @Slf4j @RequiredArgsConstructor @Getter @ToString public class PostgisTable { @Getter @AllArgsConstructor public static enum GeometryType{ MULTIPOINT("4326","geometry (MULTIPOINT,4326)","",""), POINT("4326","geometry (POINT,4326)","",""), LINE("4326","geometry (MULTILINESTRING,4326)","",""), POLYGON("4326","geometry (MULTIPOLYGON,4326)","",""); private final String SRID; private final String definition; private final String InsertWKT; private final String insertWKB; } @RequiredArgsConstructor @Getter @Setter @ToString public static class Field{ @NonNull private String name; @NonNull private FieldType type; private Boolean isIndexed; private Object constantValue; } @Getter @AllArgsConstructor public enum FieldType{ INT("int",java.sql.Types.INTEGER), BOOLEAN("boolean", java.sql.Types.BOOLEAN), TEXT("text",java.sql.Types.LONGVARCHAR), FLOAT("float",java.sql.Types.FLOAT), GEOMETRY("",0), AUTOINCREMENT("BIGSERIAL PRIMARY KEY",java.sql.Types.BIGINT); private String definition; private int sqlType; } @RequiredArgsConstructor @Getter @ToString public static class POINT{ private static Pattern pattern = Pattern.compile("(?!=\\d\\.\\d\\.)([\\d.]+)"); public static POINT parsePOINT(String point) throws DataParsingException { //POINT(8.30230113965909 44.8011688237011) // x,y try { log.debug("Parsing POINT "+point); Matcher m=pattern.matcher(point); if(!m.find()) throw new DataParsingException("Unable to get x "); Double x=Double.parseDouble(m.group(1)); if(!m.find()) throw new DataParsingException("Unable to get y "); Double y=Double.parseDouble(m.group(1)); return new POINT(x,y); }catch(Throwable t) { throw new DataParsingException("Invalid POINT "+point,t); } } @NonNull private Double x; @NonNull private Double y; } private static final NumberFormat DECIMAL_FORMAT=NumberFormat.getInstance(Locale.US); static { ((DecimalFormat) DECIMAL_FORMAT).setGroupingUsed(false); } public String getGeometryColumn() { for(Field f:fields) if(f.getType().equals(FieldType.GEOMETRY)) return f.getName(); return null; } @NonNull private String tablename; @NonNull private List fields; @NonNull private GeometryType geometryColumnType; @Setter private BBOX boundingBox=null; @Setter private POINT centroid=null; public void setTablename(String tablename) { this.tablename = sanitizeFieldName(tablename); } public String getCreateStatement() { StringBuilder stmt=new StringBuilder(); stmt.append("CREATE TABLE IF NOT EXISTS "+tablename+"( "); for(Field field:fields){ String fieldDefinition=field.getType().getDefinition(); if(field.getType().equals(FieldType.GEOMETRY)) fieldDefinition=this.getGeometryColumnType().definition; stmt.append(field.getName()+" "+fieldDefinition+","); } stmt.deleteCharAt(stmt.lastIndexOf(",")); stmt.append(")"); return stmt.toString(); } public String getDeleteByFieldStatement(Field field) { return "DELETE FROM "+tablename+" WHERE "+field.getName()+" = ? "; } public String getInsertionStatement(boolean geometryText) { StringBuilder fieldList=new StringBuilder(); StringBuilder fieldInsertion=new StringBuilder(); for(Field field:fields) { switch(field.getType()) { case AUTOINCREMENT : break; case GEOMETRY : { fieldList.append(field.getName()+","); if(geometryText) fieldInsertion.append("ST_GeomFromText(?, 4326),"); else fieldInsertion.append("ST_GeomFromWKB(?, 4326),"); break; } default : { fieldList.append(field.getName()+","); fieldInsertion.append("?,"); } } } fieldList.deleteCharAt(fieldList.lastIndexOf(",")); fieldInsertion.deleteCharAt(fieldInsertion.lastIndexOf(",")); return "Insert into "+tablename+" ("+fieldList+") VALUES ("+fieldInsertion+")"; } public void fillObjectsPreparedStatement(Map row, PreparedStatement toFill) throws SQLException { int psFieldIndex=0; HashMap rowValues=new HashMap(); for(Map.Entry entry:row.entrySet()) rowValues.put(sanitizeFieldName(entry.getKey()), entry.getValue()); for(Field field:fields) { if(!field.getType().equals(FieldType.AUTOINCREMENT)) { psFieldIndex++; Object value=rowValues.get(field.getName()); setObjectInPreparedStatement(field,value,toFill,psFieldIndex); } } } public void setObjectInPreparedStatement(Field field,Object value, PreparedStatement toFill, int psFieldIndex) throws SQLException { if(value==null) { try{ toFill.setNull(psFieldIndex, field.getType().sqlType); }catch(SQLException e) { log.error("Unable to set null for field "+field); throw e; } } else switch(field.getType()) { case FLOAT :{ toFill.setFloat(psFieldIndex, (Float)value); break; } case INT : { toFill.setInt(psFieldIndex, (Integer)value); break; } case TEXT : { toFill.setString(psFieldIndex, value.toString()); break; } case GEOMETRY : { if(value instanceof String) toFill.setString(psFieldIndex, ((String) value)); else toFill.setBytes(psFieldIndex, (byte[])value); break; } case BOOLEAN: { if(value instanceof String) toFill.setBoolean(psFieldIndex,Boolean.parseBoolean(value.toString())); if(value instanceof Boolean) toFill.setBoolean(psFieldIndex,(Boolean) value); break; } } } public void fillCSVPreparedStatament(Map row, PreparedStatement toFill,boolean explicitGeometry) throws SQLException { int psFieldIndex=0; HashMap rowValues=new HashMap(); for(Map.Entry entry:row.entrySet()) rowValues.put(sanitizeFieldName(entry.getKey()), entry.getValue()); for(Field field:fields) { if(!field.getType().equals(FieldType.AUTOINCREMENT)) { psFieldIndex++; String value=rowValues.get(field.getName()); // if(value==null||value.equalsIgnoreCase("null")) toFill.setNull(psFieldIndex, field.getType().sqlType); // else switch(field.getType()) { case FLOAT :{ try{ toFill.setFloat(psFieldIndex, Float.parseFloat(value)); }catch(NumberFormatException e) { throw new SQLException(field+" cannot be null. CSV Row is "+rowValues,e); } break; } case INT : { try{ toFill.setInt(psFieldIndex, Integer.parseInt(value)); }catch(NumberFormatException e) { log.warn("Skipping value for "+field+" row was "+rowValues,e); toFill.setNull(psFieldIndex, java.sql.Types.INTEGER); } break; } case TEXT : { toFill.setString(psFieldIndex, value.toString()); break; } case GEOMETRY : { if(explicitGeometry) { toFill.setString(psFieldIndex,value); }else { switch(geometryColumnType){ case POINT: { String xRepresentation=DECIMAL_FORMAT.format(Double.parseDouble(rowValues.get(DBConstants.Defaults.XCOORD_FIELD))); String yRepresentation=DECIMAL_FORMAT.format(Double.parseDouble(rowValues.get(DBConstants.Defaults.YCOORD_FIELD))); toFill.setString(psFieldIndex, "POINT("+xRepresentation+" "+ yRepresentation+")"); break; } default :{ toFill.setString(psFieldIndex,rowValues.get("wkt")); break; } } } break; } } } } } public static String sanitizeFieldName(String fieldName) { // return fieldName.toLowerCase().replaceAll(" ", "_").replaceAll("\\.", "").replaceAll("-", "_").replaceAll("////","_"); return fieldName.toLowerCase().replaceAll("[^a-z0-9_\\\\]", "_"); } }