001/* 002 * JKNIV, SQLegance keeping queries maintainable. 003 * 004 * Copyright (C) 2017, the original author or authors. 005 * 006 * This library is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 2.1 of the License. 010 * 011 * This library is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public 017 * License along with this library; if not, write to the Free Software Foundation, Inc., 018 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 019 */ 020package net.sf.jkniv.sqlegance.params; 021 022import java.util.Collection; 023import java.util.Collections; 024import java.util.HashMap; 025import java.util.Map; 026import java.util.regex.Matcher; 027import java.util.regex.Pattern; 028 029import org.apache.commons.beanutils.PropertyUtils; 030import org.slf4j.Logger; 031import org.slf4j.LoggerFactory; 032 033import net.sf.jkniv.reflect.BasicType; 034 035/** 036 * 037 * @author Alisson Gomes 038 * @since 0.6.0 039 */ 040public abstract class AbstractParamParser implements ParamParser 041{ 042 protected final Logger log = LoggerFactory.getLogger(getClass()); 043 // find the pattern 'a-z..0..9()*' 044 protected static final String REGEX_SINGLE_QUOTE = "|'[^']*')+"; //"\\?"; 045 046 // ":in:[\w\.?]" 047 protected static final String REGEX_IN = ":in:[\\w\\.?]+"; //FIXED bug :a.property.value -> :a 048 049 // find the pattern #{id} 050 protected static final String REGEX_HASH_MARK = "(" + "#\\{[\\w\\.?]+\\}" + // #{id} 051 "|" + REGEX_IN + REGEX_SINGLE_QUOTE; 052 053 // find the pattern :id 054 protected static final String REGEX_COLON_MARK = "(" + ":[\\w\\.?]+" + //FIXED bug :a.property.value -> :a 055 "|" + REGEX_IN + REGEX_SINGLE_QUOTE; 056 057 // find the pattern $id 058 protected static final String REGEX_DOLLAR_MARK = "(" + "\\$[\\w\\.?]+" + //FIXED bug $a.property.value -> $a 059 "|" + REGEX_IN + REGEX_SINGLE_QUOTE; 060 061 //":[\\w]+";// (:[\w]+|'[^']*')+ FIXED nao pode estar dentro de aspas 'YYYY-MM-DD HH24:MI:SS' 062 // find the pattern ? 063 protected static final String REGEX_QUESTION_MARK = "(" + "[\\?]+" + "|" + REGEX_IN + REGEX_SINGLE_QUOTE; //"\\?"; 064 065 //private static final Pattern PATTERN_HASH = Pattern.compile(REGEX_HASH_SYMBOL, Pattern.CASE_INSENSITIVE); 066 //private static final Pattern PATTERN_TWO_DOTS = Pattern.compile(REGEX_TWODOTS_SYMBOL, Pattern.CASE_INSENSITIVE); 067 //private static final Pattern PATTERN_QUESTION = Pattern.compile(REGEX_QUESTION_SYMBOL, Pattern.CASE_INSENSITIVE); 068 069 private static final Pattern PATTERN_IN = Pattern.compile(REGEX_IN, Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); 070 private static final BasicType basicType = BasicType.getInstance(); 071 072 abstract Pattern getPatternParams(); 073 074// public AbstractParamParser(String regex) 075// { 076// this.PATTERN_PARAMS = Pattern.compile(regex, Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); 077// } 078 079 @Override 080 public String replaceForPlaceholder(String query) 081 { 082 return replaceForPlaceholder(query, Collections.emptyList()); 083 } 084 085 @Override 086 public String replaceForPlaceholderWithNumber(String query, Object params) 087 { 088 StringBuffer sb = new StringBuffer(query); 089 Matcher matcherTwoDots = getPatternParams().matcher(query); 090 Map<String, String> mapForINClauseParams = new HashMap<String, String>(); 091 int startIndex = 0; 092 int endIndex = 0; 093 int index = 1; 094 while (matcherTwoDots.find()) 095 { 096 String match = matcherTwoDots.group(); 097 if (match.startsWith("'")) 098 continue; 099 if (match.startsWith(":in:")) 100 { 101 String paramName = match.substring(4, match.length()); 102 Object[] paramsAsArray = getParamsClauseIN(params, paramName); 103 if (paramsAsArray != null && paramsAsArray.length > 0) 104 { 105 StringBuilder tmp = new StringBuilder(); 106 for(int i=0; i<paramsAsArray.length; i++) 107 tmp.append( i>0? "," : "") 108 .append(getPlaceholder()+index++); 109 110 mapForINClauseParams.put(match, tmp.toString()); 111 } 112 } 113 else 114 { 115 startIndex = matcherTwoDots.start(); 116 endIndex = matcherTwoDots.end(); 117 sb.replace(startIndex, endIndex, padspace(endIndex - startIndex, index++)); 118 } 119 } 120 if (!mapForINClauseParams.isEmpty()) 121 { 122 String newSql = sb.toString(); 123 for(String key : mapForINClauseParams.keySet()) 124 newSql = newSql.replaceAll(key, mapForINClauseParams.get(key)); 125 126 sb = new StringBuffer(newSql); 127 } 128 return sb.toString(); 129 } 130 131 @Override 132 public String replaceForPlaceholder(String query, Object params) 133 { 134 StringBuffer sb = new StringBuffer(query); 135 Matcher matcherTwoDots = getPatternParams().matcher(query); 136 Map<String, String> mapForINClauseParams = new HashMap<String, String>(); 137 int startIndex = 0; 138 int endIndex = 0; 139 while (matcherTwoDots.find()) 140 { 141 String match = matcherTwoDots.group(); 142 if (match.startsWith("'")) 143 continue; 144 if (match.startsWith(":in:")) 145 { 146 String paramName = match.substring(4, match.length()); 147 Object[] paramsAsArray = getParamsClauseIN(params, paramName); 148 if (paramsAsArray!=null && paramsAsArray.length > 0) 149 { 150 StringBuilder tmp = new StringBuilder(); 151 for(int i=0; i<paramsAsArray.length; i++) 152 tmp.append( i>0? "," : "") 153 .append(getPlaceholder()); 154 155 mapForINClauseParams.put(match, tmp.toString()); 156 } 157 } 158 else 159 { 160 startIndex = matcherTwoDots.start(); 161 endIndex = matcherTwoDots.end(); 162 sb.replace(startIndex, endIndex, padspace(endIndex - startIndex, -1)); 163 } 164 } 165 if (!mapForINClauseParams.isEmpty()) 166 { 167 String newSql = sb.toString(); 168 for(String key : mapForINClauseParams.keySet()) 169 newSql = newSql.replaceAll(key, mapForINClauseParams.get(key)); 170 171 sb = new StringBuffer(newSql); 172 } 173 return sb.toString(); 174 } 175 176 @Override 177 public String getPlaceholder() 178 { 179 return "?"; 180 } 181 182 protected String padspace(int size, int index) 183 { 184 StringBuffer s = new StringBuffer(getPlaceholder() +(index < 0 ? "" : index)); 185 int newSize = (index < 0 ? size : size - String.valueOf(index).length());// discount the length string from index 186 for (int i = 1; i < newSize; i++) 187 s.append(" "); 188 return s.toString(); 189 } 190 191 protected Object[] getParamsClauseIN(Object params, String name) 192 { 193 Object[] paramsClauseIN = getParamsAsArray(params); 194 if (paramsClauseIN == null) 195 { 196 try 197 { 198 Object value = PropertyUtils.getProperty(params, name); 199 if (value != null && (value instanceof String || basicType.isNumberType(value.getClass()))) 200 paramsClauseIN = new Object[]{ value }; 201 else 202 paramsClauseIN = getParamsAsArray(value); 203 } 204 catch (Exception e)//IllegalAccessException, InvocationTargetException, NoSuchMethodException 205 { 206 log.warn("Cannot read property [{}] for object [{}]. cause={}", name, 207 (params == null ? "null" : params.toString()), e.getMessage()); 208 } 209 } 210 return paramsClauseIN; 211 } 212 213 private Object[] getParamsAsArray(Object params) 214 { 215 Object[] array = null; 216 if (params instanceof Collection) 217 array = ((Collection<?>) params).toArray(); 218 else if (params != null && params.getClass().isArray()) 219 { 220 array = (Object[]) params; 221 } 222 return array; 223 } 224 225}