package cn.org.rapid_framework.generator.ext.tableconfig; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import cn.org.rapid_framework.generator.provider.db.sql.model.SqlSegment; import cn.org.rapid_framework.generator.util.StringHelper; import cn.org.rapid_framework.generator.util.XMLHelper; import cn.org.rapid_framework.generator.util.sqlparse.SqlParseHelper; /** * 解析ibatis的sql语句,并转换成正常的sql. * * 主要功能: * 1.将动态构造条件节点全部替换成删除 只留下可执行的SQL * */ public class IbatisSqlMapConfigParser { public Map<String,SqlSegment> usedIncludedSqls = new HashMap(); //增加一条sql语句引用的 includesSql的解析 private String sourceSql; private Map<String,String> includeSqls; public String resultSql; public String parse(String str) { return parse(str,new HashMap()); } public String parse(String str,Map<String,String> includeSqls) { this.sourceSql = str; this.includeSqls = includeSqls; str = Helper.removeComments("<for_remove_comment>"+str+"</for_remove_comment>"); str = Helper.removeSelectKeyXmlForInsertSql(str); Pattern xmlTagRegex = Pattern.compile("<(/?[\\w#@]+)(.*?)>"); StringBuffer sql = new StringBuffer(); Matcher m = xmlTagRegex.matcher(str); OpenCloseTag openClose = null; Map previousTagAttributes = null; while(m.find()) { String xmlTag = m.group(1); String attributesString = m.group(2); Map<String,String> attributes = XMLHelper.parse2Attributes(attributesString); if("include".equals(xmlTag.trim())) { processIncludeByRefid(includeSqls, sql, m, attributes); continue; } MybatisHelper.processForMybatis(sql, xmlTag, attributes); String replacement = Helper.getReplacement(attributes.get("open"), attributes.get("prepend")); StringHelper.appendReplacement(m, sql, replacement); openClose = processOpenClose(sql, openClose, xmlTag, attributes); MybatisHelper.processMybatisForeachCloseTag(sql, previousTagAttributes, xmlTag); previousTagAttributes = attributes; } //FIXME 不能兼容自动删除分号, 因为还需要测试最终的ibatis sql是否会删除; resultSql = StringHelper.unescapeXml(StringHelper.removeXMLCdataTag(SqlParseHelper.replaceWhere(sql.toString()))); return resultSql; // return StringHelper.unescapeXml(StringHelper.removeXMLCdataTag(SqlParseHelper.replaceWhere(sql.toString()))).replace(";", ""); } private OpenCloseTag processOpenClose(StringBuffer sql, OpenCloseTag openClose, String xmlTag, Map<String, String> attributes) { if(openClose != null && openClose.close != null && xmlTag.equals("/"+openClose.xmlTag)) { sql.append(openClose.close); openClose = null; } if(attributes.get("close") != null) { openClose = new OpenCloseTag(); // FIXME 未处理多个open close问题 openClose.xmlTag = xmlTag; openClose.close = attributes.get("close"); } return openClose; } public List<SqlSegment> getSqlSegments() { return new ArrayList(usedIncludedSqls.values()); } private static class OpenCloseTag { public String close; public String xmlTag; } //process <include refid="otherSql"/> private void processIncludeByRefid(Map<String, String> includeSqls, StringBuffer sb, Matcher m, Map<String, String> attributes) { String refid = attributes.get("refid"); if(refid == null) { m.appendReplacement(sb, ""); }else { String includeValue = includeSqls.get(refid); if(includeValue == null) throw new IllegalArgumentException("not found include sql by <include refid='"+refid+"'/>"); String parsedIncludeValue = parse(includeValue,includeSqls); usedIncludedSqls.put(refid, new SqlSegment(refid,includeValue,parsedIncludeValue)); StringHelper.appendReplacement(m, sb, parsedIncludeValue); } } static class MybatisHelper { private static void processMybatisForeachCloseTag(StringBuffer sql, Map preTagAttributes, String xmlTag) { // mybatis username in <foreach collection="usernameList" item="item" index="index" open="(" separator="," close=")">#{item}<foreach> if ("/foreach".equals(xmlTag.trim())) { // 将 username in (#{item}) 替换为 username in (#collection[]#) String item = (String)preTagAttributes.get("item"); String collection = (String)preTagAttributes.get("collection"); String tempSql = StringHelper.replace(sql.toString(), "#{"+item+"}", "#"+collection+"[]#"); tempSql = StringHelper.replace(tempSql.toString(), "${"+item+"}", "$"+collection+"[]$"); sql.setLength(0); sql.append(tempSql); } } private static void processForMybatis(StringBuffer sb, String xmlTag, Map<String, String> attributes) { // mybatis <where> if ("where".equals(xmlTag.trim())) { attributes.put("open", "where"); } // mybatis <set> if ("set".equals(xmlTag.trim())) { attributes.put("open", "set"); } // mybatis <foreach collection="usernameList" item="item" index="index" open="(" separator="," close=")"> if ("foreach".equals(xmlTag.trim())) { // m.appendReplacement(sb, "set"); //FIXME for foreach } // mybatis <trim prefix="" suffix="" prefixOverrides="" // suffixOverrides=""></trim> if ("trim".equals(xmlTag.trim())) { attributes.put("open", attributes.get("prefix")); attributes.put("close", attributes.get("suffix")); // FIXME for // prefixOverrides,suffixOverrides // <trim prefix="SET" suffixOverrides=","> // <trim prefix="WHERE" prefixOverrides="AND |OR "> } } } static class Helper { private static String getReplacement(String open, String prepend) { String replacement = null; if(prepend != null) { replacement = " "+prepend+" "+StringHelper.defaultString(open); }else { if (StringHelper.isEmpty(open)) { replacement = ""; } else { replacement = (" " + open); } } return replacement; } private static String removeSelectKeyXmlForInsertSql(String str) { if(str == null) return null; return str.replaceAll("(?s)<selectKey.*?>.*</selectKey>",""); } public static String removeComments(String str) { if(str == null) return null; str = str.replaceAll("(?s)<!--.*?-->", "").replaceAll("(?s)/\\*.*?\\*/", "").replace("query not allowed", ""); return str; } } }