package com.freetmp.mbg.plugin.geom;
import com.freetmp.mbg.plugin.upsert.AbstractUpsertPlugin;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.PluginAdapter;
import org.mybatis.generator.api.dom.OutputUtilities;
import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
import org.mybatis.generator.api.dom.xml.*;
import org.mybatis.generator.codegen.mybatis3.MyBatis3FormattingUtilities;
import org.mybatis.generator.internal.db.ConnectionFactory;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/*
* Postgis的地理信息插件
* @author Pin Liu
*/
public class PostgisGeoPlugin extends PluginAdapter {
public static final String SRID_NAME = "srid";
public static final String lineSeparator;
static {
String ls = System.getProperty("line.separator"); //$NON-NLS-1$
if (ls == null) {
ls = "\n"; //$NON-NLS-1$
}
lineSeparator = ls;
}
Connection connection;
Field elementsList;
String srid;
@Override
public boolean validate(List<String> warnings) {
boolean valid = true;
srid = properties.getProperty(SRID_NAME);
if(StringUtils.isEmpty(srid)){
srid = "3857";
}
if(connection == null){
try {
connection = ConnectionFactory.getInstance().getConnection(context.getJdbcConnectionConfiguration());
} catch (SQLException e) {
e.printStackTrace();
valid = false;
}
}
elementsList = FieldUtils.getField(XmlElement.class, "elements", true);
return valid;
}
/*
* 在初始化阶段,检查所有字段的JdbcType为OTHER的字段,获取其类型名称(TYPE_NAME)
* 根据其类型名称确定其实际的地理信息类型
*/
@Override
public void initialized(IntrospectedTable introspectedTable) {
for(IntrospectedColumn introspectedColumn : introspectedTable.getAllColumns()){
if(introspectedColumn.getJdbcType() == Types.OTHER){
String typeName = fetchTypeName(introspectedColumn);
//System.out.println("postgis type : "+typeName);
switch(typeName.toLowerCase()){
case "geometry":{
introspectedColumn.setFullyQualifiedJavaType(new FullyQualifiedJavaType("org.geolatte.geom.Geometry"));
//introspectedColumn.setJdbcTypeName(typeName.toUpperCase());
break;
}
}
}
}
}
@Override
public boolean sqlMapDocumentGenerated(Document document, IntrospectedTable introspectedTable) {
List<IntrospectedColumn> columns = new ArrayList<IntrospectedColumn>();
for(IntrospectedColumn introspectedColumn : introspectedTable.getAllColumns()){
if(introspectedColumn.getFullyQualifiedJavaType().getPackageName().equalsIgnoreCase("org.geolatte.geom")){
columns.add(introspectedColumn);
}
}
if(columns.isEmpty()) return true;
XmlElement xmlElement = document.getRootElement();
for(Element element : xmlElement.getElements()){
if(element instanceof XmlElement){
XmlElement xe = (XmlElement) element;
switch (xe.getName().toLowerCase()) {
case "sql":
if(containsAttribute(xe, "id", introspectedTable.getBaseColumnListId())){
checkAndReplaceOutput(columns,xe);
}else if(containsAttribute(xe, "id", AbstractUpsertPlugin.IDENTIFIERS_ARRAY_CONDITIONS)){
checkAndReplaceInput(columns, xe);
}
break;
case "insert":
case "update":
checkAndReplaceInput(columns, xe);
break;
default:
break;
}
}
}
return true;
}
/*
* 检查并替换输入的地理信息相关参数,使用PostGis提供的ST_GeomFromText函数
* @author Pin Liu
*/
protected void checkAndReplaceInput(List<IntrospectedColumn> columns, XmlElement xe){
for(Element element : xe.getElements()){
if(element instanceof XmlElement){
checkAndReplaceInput(columns, (XmlElement) element);
}
if(element instanceof TextElement){
TextElement te = (TextElement) element;
checkAndReplaceInput(columns, te);
}
}
}
protected void checkAndReplaceInput(List<IntrospectedColumn> columns, TextElement te) {
String sql = te.getContent();
for(IntrospectedColumn column : columns){
if(column.getFullyQualifiedJavaType().getShortName().equals("Geometry")){
String paramStr = MyBatis3FormattingUtilities.getParameterClause(column);
sql = StringUtils.replace(sql, paramStr, "ST_GeomFromText(" + paramStr + ","+srid+")"); //replace no prefix geo relate column
paramStr = MyBatis3FormattingUtilities.getParameterClause(column, "record.");
sql = StringUtils.replace(sql, paramStr, "ST_GeomFromText(" + paramStr + ","+srid+")"); //replace mbg generate prefix geo relate column
paramStr = MyBatis3FormattingUtilities.getParameterClause(column, "item.");
sql = StringUtils.replace(sql, paramStr, "ST_GeomFromText(" + paramStr + ","+srid+")"); //replace mbg batch plugin generate prefix geo relate column
// System.out.println();
// System.out.println(sql);
}
}
try {
FieldUtils.writeDeclaredField(te, "content", sql, true);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
/*
* 检查并替换输出的地理信息相关参数,使用PostGis提供的ST_AsText函数
* @author Pin Liu
*/
protected void checkAndReplaceOutput(List<IntrospectedColumn> columns, XmlElement xe) {
for(Element element : xe.getElements()){
if(element instanceof XmlElement){
checkAndReplaceOutput(columns, (XmlElement) element);
}
if(element instanceof TextElement){
TextElement te = (TextElement) element;
checkAndReplaceOutput(columns, te);
}
}
}
protected void checkAndReplaceOutput(List<IntrospectedColumn> columns, TextElement te) {
String sql = te.getContent();
for(IntrospectedColumn column : columns){
if(column.getFullyQualifiedJavaType().getShortName().equals("Geometry")){
String columnStr = null;
if(column.isColumnNameDelimited()){
columnStr = "\""+column.getActualColumnName()+"\"";
}else{
columnStr = column.getActualColumnName();
}
sql = StringUtils.replaceOnce(sql, columnStr, "ST_AsText("+columnStr+") as " + columnStr);
//sql = sql.replace(column.getActualColumnName(), "ST_AsText("+column.getActualColumnName()+")");
// System.out.println();
// System.out.println(sql);
}
}
try {
FieldUtils.writeDeclaredField(te, "content", sql, true);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
/*
* 使用新的sql语句替换原来的xml内容
* @author Pin Liu
*/
protected void replaceOriginChildElements(XmlElement xe, String sql) {
sql = sql.trim();
String[] lines = sql.split(lineSeparator);
List<Element> elements = Lists.newArrayList();
for(String line : lines){
elements.add(new TextElement(line));
}
try {
elementsList.set(xe, elements);
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
}
/*
* 获取xml标签内部的内容
* @author Pin Liu
*/
protected String getContentWithoutOuterTags(XmlElement xe){
int indentLevel = 0;
StringBuilder sb = new StringBuilder();
Iterator<Element> iter = xe.getElements().iterator();
while(iter.hasNext()){
sb.append(iter.next().getFormattedContent(indentLevel));
if(iter.hasNext()){
OutputUtilities.newLine(sb);
}
}
return sb.toString();
}
/*
* 检查xml标签元素是否包含指定属性
* @author Pin Liu
*/
public boolean containsAttribute(XmlElement xe,String key, String value){
if(xe.getAttributes() != null){
for(Attribute attribute : xe.getAttributes()){
if(attribute.getName().equalsIgnoreCase(key) && attribute.getValue().equalsIgnoreCase(value)){
return true;
}
}
}
return false;
}
/*
* 根据MBG探测到的数据库表的元数据获取字段的类别名称
* @author Pin Liu
*/
public String fetchTypeName(IntrospectedColumn introspectedColumn){
String columnName = introspectedColumn.getActualColumnName();
String catalog = introspectedColumn.getIntrospectedTable().getTableConfiguration().getCatalog();
String schema = introspectedColumn.getIntrospectedTable().getTableConfiguration().getSchema();
String table = introspectedColumn.getIntrospectedTable().getTableConfiguration().getTableName();
String dataType = null;
try {
ResultSet set = connection.getMetaData().getColumns(catalog, schema, table, columnName);
while(set.next()){
dataType = set.getString("TYPE_NAME");
}
} catch (SQLException e) {
e.printStackTrace();
}
return dataType;
}
}