1 package com.randomnoun.common.spring;
2
3
4
5
6
7 import java.sql.ResultSet;
8 import java.sql.SQLException;
9 import java.util.*;
10
11 import org.springframework.dao.DataAccessException;
12 import org.springframework.jdbc.core.*;
13 import org.apache.log4j.Logger;
14
15 import com.randomnoun.common.Struct;
16
17
18
19
20
21
22
23
24 public class StructuredMapCallbackHandlerResultSetExtractor
25 implements ResultSetExtractor<Object> {
26
27
28
29
30 private static Logger logger = Logger.getLogger(StructuredMapCallbackHandlerResultSetExtractor.class);
31
32
33 Map<String, String> columnMapping;
34
35
36 private final List<Map<String, Object>> results;
37 private List<String> levels;
38
39
40 private final RowMapper<Map<String, Object>> rowMapper;
41 private Map<String, Object> lastResultRow = null;
42
43 private StructuredMapCallbackHandler smch;
44
45 public static interface StructuredMapCallbackHandler {
46 public void processMap(Map<String, Object> row);
47 }
48
49
50 private int rowNum = 0;
51
52
53
54
55
56
57 public StructuredMapCallbackHandlerResultSetExtractor(JdbcTemplate jt, String mappings, StructuredMapCallbackHandler smch) {
58 this(new ColumnMapRowMapper(), mappings, smch);
59 }
60
61
62
63
64
65
66
67 private StructuredMapCallbackHandlerResultSetExtractor(RowMapper<Map<String, Object>> rowMapper, String mappings, StructuredMapCallbackHandler smch) {
68 this.smch = smch;
69 if (mappings == null) { throw new NullPointerException("mappings cannot be null"); }
70
71
72
73 this.results = new ArrayList<Map<String, Object>>();
74 this.rowMapper = rowMapper;
75 this.columnMapping = new HashMap<String, String>();
76 this.levels = new ArrayList<String>(3);
77
78 StringTokenizer st = new StringTokenizer(mappings, ",");
79 StringTokenizer st2;
80 StringTokenizer st3;
81 String column = null;
82 String columnTarget = null;
83 String token;
84 String mapping;
85
86 while (st.hasMoreTokens()) {
87 mapping = st.nextToken().trim();
88
89 if (mapping.indexOf(' ') == -1) {
90 column = mapping;
91 columnTarget = mapping;
92 } else {
93
94
95
96
97
98
99
100 int state = 0;
101 st2 = new StringTokenizer(mapping, " ");
102 while (st2.hasMoreTokens()) {
103 token = st2.nextToken().trim();
104 if (token.equals("")) { continue; }
105
106 if (state == 0) {
107 column = token;
108 state = 1;
109 } else if (state == 1) {
110 if (!token.equalsIgnoreCase("as")) {
111 throw new IllegalArgumentException("Invalid mapping '" + mapping + "'; expected AS");
112 }
113 state = 2;
114 } else if (state == 2) {
115 columnTarget = token;
116 state = 3;
117 } else if (state == 3) {
118 throw new IllegalArgumentException("Invalid mapping '" + mapping + "'; too many tokens");
119 }
120 }
121 }
122
123
124 int levelIdx = 0;
125 st3 = new StringTokenizer(columnTarget, ".");
126 if (st3.hasMoreTokens()) {
127 String level = st3.nextToken();
128
129 while (st3.hasMoreTokens()) {
130 if (levelIdx < levels.size()) {
131 if (!levels.get(levelIdx).equals(level)) {
132 throw new IllegalArgumentException("Multiple lists in mapping at level " + levelIdx + ": '" + levels.get(levelIdx) + "' and '" + level + "'");
133 }
134 } else {
135 levels.add(level);
136
137 }
138 level = st3.nextToken();
139 levelIdx++;
140 }
141 }
142 columnMapping.put(column.toUpperCase(), columnTarget);
143 }
144 }
145
146
147
148
149
150
151
152 public Object extractData(ResultSet rs) throws SQLException, DataAccessException
153 {
154 while (rs.next()) {
155 processRow(rs);
156 }
157 if (results.size()>0) { smch.processMap(results.get(0)); }
158 return null;
159 }
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180 @SuppressWarnings("unchecked")
181 public void processRow(ResultSet rs)
182 throws SQLException {
183 Map<String, Object> row = (Map<String, Object>) rowMapper.mapRow(rs, this.rowNum++);
184
185
186
187 int createLevel = 0;
188 List<Map<String, Object>> createList = results;
189 Map<String, Object> createRow = new HashMap<String, Object>();
190 String createPrefix = "";
191
192
193 if (lastResultRow != null) {
194
195 createLevel = levels.size() + 1;
196
197
198 for (Iterator<Map.Entry<String, String>> i = columnMapping.entrySet().iterator(); i.hasNext();) {
199 Map.Entry<String, String> entry = (Map.Entry<String, String>) i.next();
200 String column = (String) entry.getKey();
201 String columnTarget = (String) entry.getValue();
202
203 List<Map<String,Object>> containerList = results;
204 Map<String, Object> containerMap = lastResultRow;
205
206 String component;
207 int pos = columnTarget.indexOf('.');
208 int level = 0;
209
210 while (pos != -1) {
211 component = columnTarget.substring(0, pos);
212 columnTarget = columnTarget.substring(pos + 1);
213
214 if (!containerMap.containsKey(component)) {
215 throw new IllegalStateException("Missing field '" + component + "' in " + Struct.structuredMapToString("containerMap", containerMap) + "; last result row is " + Struct.structuredMapToString("lastResultRow", lastResultRow));
216 }
217
218 if (component.equals(levels.get(level))) {
219 level++;
220 containerList = (List<Map<String,Object>>) containerMap.get(component);
221 containerMap = (Map<String, Object>) containerList.get(containerList.size() - 1);
222 if (containerMap==null) {
223 logger.error("null containerMap");
224 }
225 } else {
226 containerMap = (Map<String, Object>) containerMap.get(component);
227 if (containerMap==null) {
228 logger.error("null containerMap");
229 }
230
231 }
232
233 pos = columnTarget.indexOf('.');
234 }
235
236 Object thisValue = row.get(column);
237 Object lastValue = containerMap.get(columnTarget);
238
239
240 if ((thisValue == null && lastValue != null) || (thisValue != null && !thisValue.equals(lastValue))) {
241
242 if (createLevel > level) {
243 createList = containerList;
244
245
246
247 createLevel = level;
248 }
249 }
250 }
251 }
252
253 if (createLevel > levels.size()) {
254
255 return;
256 }
257
258 for (int i = 0; i < createLevel; i++) {
259 createPrefix = createPrefix + levels.get(i) + ".";
260 }
261
262
263 for (Iterator<Map.Entry<String, String>> i = columnMapping.entrySet().iterator(); i.hasNext();) {
264 Map.Entry<String, String> entry = i.next();
265 String column = (String) entry.getKey();
266 String columnTarget = (String) entry.getValue();
267 if (!columnTarget.startsWith(createPrefix)) {
268 continue;
269 }
270 Object value = row.get(column);
271
272
273
274 columnTarget = columnTarget.substring(createPrefix.length());
275
276
277 List<Map<String, Object>> containerList = createList;
278 Map<String, Object> containerMap = createRow;
279
280 int level = createLevel;
281 String component;
282 int pos = columnTarget.indexOf('.');
283
284 while (pos != -1) {
285 component = columnTarget.substring(0, pos);
286 columnTarget = columnTarget.substring(pos + 1);
287
288 if (component.equals(levels.get(level))) {
289 level++;
290 containerList = (List<Map<String, Object>>) containerMap.get(component);
291
292 if (containerList == null) {
293 containerList = new ArrayList<Map<String, Object>>();
294 containerMap.put(component, containerList);
295 containerList.add(new HashMap<String, Object>());
296 }
297
298 containerMap = (Map<String, Object>) containerList.get(containerList.size() - 1);
299 if (containerMap==null) {
300 logger.error("C null containerMap");
301 }
302
303 } else {
304 containerMap = (Map<String, Object>) containerMap.get(component);
305 if (containerMap==null) {
306 logger.error("D null containerMap");
307 }
308
309 }
310
311 pos = columnTarget.indexOf('.');
312 }
313
314 containerMap.put(columnTarget, value);
315 }
316
317 if (createList == results) {
318 if (results.size()>0) { smch.processMap(results.get(0)); }
319 results.clear();
320 } else {
321
322 }
323
324 createList.add(createRow);
325 lastResultRow = results.get(results.size() - 1);
326
327 }
328
329 }