001package net.sf.jkniv.whinstone.couchdb.statement; 002 003import java.io.IOException; 004import java.util.ArrayList; 005import java.util.Collections; 006import java.util.List; 007import java.util.regex.Matcher; 008import java.util.regex.Pattern; 009 010import org.slf4j.Logger; 011import org.slf4j.LoggerFactory; 012 013import com.fasterxml.jackson.core.JsonParseException; 014import com.fasterxml.jackson.databind.JsonMappingException; 015 016import net.sf.jkniv.exception.HandlerException; 017import net.sf.jkniv.sqlegance.RepositoryException; 018import net.sf.jkniv.sqlegance.logger.DataMasking; 019import net.sf.jkniv.sqlegance.params.ParamParser; 020import net.sf.jkniv.whinstone.Param; 021import net.sf.jkniv.whinstone.ResultRow; 022import net.sf.jkniv.whinstone.couchdb.HttpBuilder; 023import net.sf.jkniv.whinstone.couchdb.commands.JsonMapper; 024import net.sf.jkniv.whinstone.statement.AutoKey; 025import net.sf.jkniv.whinstone.statement.StatementAdapter; 026 027/** 028 * 029 * @author Alisson Gomes 030 * @since 0.6.2 031 */ 032public class CouchDbStatementAdapter<T, R> implements StatementAdapter<T, String> 033{ 034 private static final Logger LOG = LoggerFactory.getLogger(CouchDbStatementAdapter.class); 035 private static final Logger SQLLOG = net.sf.jkniv.whinstone.couchdb.LoggerFactory.getLogger(); 036 private static final DataMasking MASKING = net.sf.jkniv.whinstone.couchdb.LoggerFactory.getDataMasking(); 037 protected static final String REGEX_QUESTION_MARK = "[\\?]+"; //"\\?"; 038 protected static final Pattern PATTERN_QUESTION = Pattern.compile(REGEX_QUESTION_MARK, Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); 039 private final HandlerException handlerException; 040 private int index; 041 private String body; 042 private boolean boundParams; 043 private List<Param> params; 044 045 public CouchDbStatementAdapter(HttpBuilder httpBuilder, String body, ParamParser paramParser)//HttpRequestBase request) 046 { 047 this.body = body; 048 this.params = new ArrayList<Param>(); 049 this.boundParams = false; 050 this.handlerException = new HandlerException(RepositoryException.class, "Cannot set parameter [%s] value [%s]"); 051 this.reset(); 052 configHanlerException(); 053 } 054 055 public String getBody() 056 { 057 return this.body; 058 } 059 060 private void configHanlerException() 061 { 062 // JsonParseException | JsonMappingException | IOException 063 handlerException.config(JsonParseException.class, "Error to parser json non-well-formed content [%s]"); 064 handlerException.config(JsonMappingException.class, "Error to deserialization content [%s]"); 065 handlerException.config(IOException.class, "Error from I/O json content [%s]"); 066 } 067 068 @Override 069 public StatementAdapter<T, String> with(ResultRow<T, String> resultRow) 070 { 071 //this.resultRow = resultRow; TODO implements resultrow for couchbase 072 return this; 073 } 074 075 @Override 076 public StatementAdapter<T, String> bind(String name, Object value) 077 { 078 int index = currentIndex();//increment index 079 this.params.add(new Param(value, name, index)); 080 return this; 081 } 082 083 @Override 084 public StatementAdapter<T, String> bind(Param value) 085 { 086 this.params.add(value); 087 return this; 088 } 089 090 @Override 091 public StatementAdapter<T, String> bind(Param... values) 092 { 093 for (Param v : values) 094 { 095 this.params.add(v); 096 } 097 return this; 098 } 099 100 @Override 101 public List<T> rows() 102 { 103 bindParams(); 104 return Collections.emptyList(); 105 } 106 107 @Override 108 public void bindKey() 109 { 110 // FIXME UnsupportedOperationException 111 throw new UnsupportedOperationException("CouchDb repository doesn't implement this method yet!"); 112 } 113 114 @Override 115 public StatementAdapter<T, String> with(AutoKey generateKey) 116 { 117 return this; 118 } 119 120 @Override 121 public int execute() 122 { 123 // FIXME UnsupportedOperationException 124 throw new UnsupportedOperationException("CouchDb repository doesn't implement this method yet!"); 125 } 126 127 @Override 128 public int reset() 129 { 130 int before = index; 131 index = 1; 132 return before; 133 } 134 135 private void bindParams() 136 { 137 StringBuilder json = new StringBuilder(); 138 logParams(); 139 if (!boundParams) 140 { 141 Matcher matcherQuestion = PATTERN_QUESTION.matcher(this.body); 142 int i = 0; 143 int start = 0, endBody = this.body.length(); 144 while (matcherQuestion.find()) 145 { 146 json.append(body.substring(start, matcherQuestion.start())); 147 json.append(JsonMapper.mapper(params.get(i++).getValue())); 148 //System.out.printf("group[%s] [%d,%d]\n", matcherQuestion.group(), matcherQuestion.start(), matcherQuestion.end()); 149 params.add(i++, new Param(body.subSequence(matcherQuestion.start(), matcherQuestion.end()).toString(), i-1)); 150 start = matcherQuestion.end(); 151 } 152 json.append(body.substring(start, endBody)); 153 //System.out.printf("%s\n", json); 154 this.body = json.toString(); 155 } 156 this.boundParams = true; 157 } 158 159 /** 160 * return the index and increment the next value 161 * <b>Note: take care with debug invoke, this method increment the index</b> 162 * @return the current index 163 */ 164 private int currentIndex() 165 { 166 return index++; 167 } 168 169 private void logParams() 170 { 171 172 if (SQLLOG.isDebugEnabled()) 173 { 174 for(Param param : params) 175 SQLLOG.debug("Setting SQL Parameter from index [{}] with name [{}] with value of [{}] type of [{}]", param.getIndex(), 176 param.getName(), MASKING.mask(param.getName(), param.getValue()), (param.getValue() == null ? "NULL" : param.getValue().getClass())); 177 } 178 } 179 180 @Override 181 public void close() 182 { 183 // there isn't statement to close 184 } 185 186 @Override 187 public void setFetchSize(int rows) 188 { 189 LOG.warn("Couchdb doesn't support fetch size!"); 190 } 191}