001/* 002 * JKNIV, whinstone one contract to access your database. 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.whinstone.couchdb.commands; 021 022import java.io.IOException; 023import java.util.ArrayList; 024import java.util.Collections; 025import java.util.Iterator; 026import java.util.List; 027import java.util.Map; 028 029import org.slf4j.Logger; 030import org.slf4j.LoggerFactory; 031 032import com.fasterxml.jackson.core.JsonParser; 033import com.fasterxml.jackson.core.JsonProcessingException; 034import com.fasterxml.jackson.core.ObjectCodec; 035import com.fasterxml.jackson.databind.DeserializationContext; 036import com.fasterxml.jackson.databind.JsonDeserializer; 037import com.fasterxml.jackson.databind.JsonNode; 038 039import net.sf.jkniv.reflect.BasicType; 040import net.sf.jkniv.reflect.beans.ObjectProxy; 041import net.sf.jkniv.reflect.beans.ObjectProxyFactory; 042import net.sf.jkniv.reflect.beans.PropertyAccess; 043import net.sf.jkniv.whinstone.Queryable; 044import net.sf.jkniv.whinstone.couchdb.CouchDbResult; 045import net.sf.jkniv.whinstone.couchdb.ExecutionStats; 046 047@SuppressWarnings({"unchecked", "rawtypes" }) 048public class CouchDbJsonDeserialization extends JsonDeserializer<CouchDbResultImpl> 049{ 050 private final static Logger LOG = LoggerFactory.getLogger(CouchDbJsonDeserialization.class); 051 private final static PropertyAccess DEFAULT_ACCESS_ID = new PropertyAccess("id"); 052 053 @Override 054 public CouchDbResultImpl deserialize(JsonParser json, DeserializationContext ctxt) 055 throws IOException, JsonProcessingException 056 { 057 ObjectCodec codec = json.getCodec(); 058 JsonNode field = codec.readTree(json); 059 Queryable queryable = JsonMapper.getCurrentQuery(); 060 Class<?> returnType = queryable.getReturnType(); 061 PropertyAccess accessId = DEFAULT_ACCESS_ID; 062 if (queryable.getDynamicSql() != null) 063 accessId = queryable.getDynamicSql().getSqlDialect().getAccessId(); 064 065 final Long totalRows = 0L; 066 final Long offset = 0L; 067 String bookmark = null; 068 String warning = null; 069 List rows = Collections.emptyList(); 070 ExecutionStats stats = null; 071 if (field.hasNonNull("bookmark")) 072 bookmark = field.get("bookmark").asText(); 073 074 if (field.hasNonNull("execution_stats")) 075 stats = JsonMapper.mapper(field.get("execution_stats").asText(), ExecutionStats.class); 076 077 if (field.hasNonNull("warning")) 078 { 079 warning = field.get("warning").asText(); 080 LOG.warn("Query [{}] warnning message: {}", queryable.getName(), warning); 081 } 082 083 if (field.has("rows")) 084 { 085 final JsonNode nodeRows = field.get("rows"); 086 if (CouchDbResult.class.isAssignableFrom(returnType)) 087 rows = nodeToMap(nodeRows); 088 else 089 rows = parserViewResult(nodeRows, returnType, accessId); 090 } 091 else if (field.has("docs")) 092 { 093 final JsonNode nodeDocs = field.get("docs"); 094 if (CouchDbResult.class.isAssignableFrom(returnType)) 095 rows = nodeToMap(nodeDocs); 096 else 097 rows = parserFindResult(nodeDocs, returnType, accessId); 098 } 099 return CouchDbResultImpl.of(totalRows, offset, bookmark, warning, rows, stats); 100 } 101 102 private List parserViewResult(final JsonNode nodeRows, Class<?> returnType, PropertyAccess accessId) 103 { 104 final List rows = new ArrayList(); 105 if (nodeRows.isArray()) 106 { 107 Iterator<JsonNode> it = nodeRows.elements(); 108 while (it.hasNext()) 109 { 110 JsonNode element = it.next(); 111 JsonNode row = element.get("value"); 112 if (element.has("doc")) 113 row = element.get("doc"); 114 115 Object object = null; 116 if (BasicType.getInstance().isBasicType(returnType)) 117 { 118 if (!row.isNull()) 119 { 120 if(row.isInt()) 121 object = row.asInt(); 122 else if(row.isLong()) 123 object = row.asLong(); 124 else if(row.isDouble()) 125 object = row.asDouble(); 126 else if (row.isBoolean()) 127 object = row.asBoolean(); 128 else if (row.isTextual()) 129 object = row.asText(); 130 } 131 } 132 else if (row.isObject()) 133 object = parserRow(returnType, accessId, element, row); 134 else 135 object = JsonMapper.mapper(element.toString(), returnType); 136 rows.add(object); 137 } 138 } 139 return rows; 140 } 141 142 private List parserFindResult(final JsonNode nodeRows, Class<?> returnType, PropertyAccess accessId) 143 { 144 final List rows = new ArrayList(); 145 if (nodeRows.isArray()) 146 { 147 Iterator<JsonNode> it = nodeRows.elements(); 148 while (it.hasNext()) 149 { 150 JsonNode row = it.next(); 151 Object object = parserRow(returnType, accessId, null, row); 152 rows.add(object); 153 } 154 } 155 return rows; 156 } 157 158 private Object parserRow(Class<?> returnType, PropertyAccess accessId, JsonNode element, JsonNode row) 159 { 160 Object object = null; 161 if (row.isObject()) 162 { 163 object = JsonMapper.mapper(row.toString(), returnType); 164 if (element != null) 165 setIdentity(element, object, accessId); 166 } 167 else 168 { 169 LOG.error("the value node from json result isn't an object. Cannot parse it to returnType {}", returnType); 170 } 171 return object; 172 } 173 174 private void setIdentity(JsonNode element, Object row, PropertyAccess accessId) 175 { 176 String id = null; 177 String key = null; 178 if (element.hasNonNull("id")) 179 id = element.get("id").asText(); 180 if (element.hasNonNull("key")) 181 key = element.get("key").asText(); 182 183 if (row instanceof Map) 184 { 185 ((Map) row).put("id", id); 186 ((Map) row).put("key", key); 187 } 188 else 189 { 190 ObjectProxy<?> proxy = ObjectProxyFactory.of(row); 191 if (proxy.hasMethod(accessId.getWriterMethodName())) 192 proxy.invoke(accessId.getWriterMethodName(), id); 193 if (proxy.hasMethod("setKey")) 194 proxy.invoke("setKey", key); 195 } 196 197 } 198 199 private List nodeToMap(final JsonNode nodeRows) 200 { 201 final List rows = new ArrayList(); 202 Iterator<JsonNode> it = nodeRows.elements(); 203 while (it.hasNext()) 204 { 205 JsonNode row = it.next(); 206 Map map = JsonMapper.mapper(row.toString(), Map.class); 207 rows.add(map); 208 } 209 return rows; 210 } 211}