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}