1 package org.synchronoss.cpo.core.meta;
2
3 /*-
4 * [[
5 * core
6 * ==
7 * Copyright (C) 2003 - 2026 Exaxis LLC, Synchronoss Technologies Inc
8 * ==
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU Lesser General Public License as
11 * published by the Free Software Foundation, either version 3 of the
12 * License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Lesser Public License for more details.
18 *
19 * You should have received a copy of the GNU General Lesser Public
20 * License along with this program. If not, see
21 * <http://www.gnu.org/licenses/lgpl-3.0.html>.
22 * ]]
23 */
24
25 import java.lang.reflect.InvocationTargetException;
26 import org.synchronoss.cpo.core.CpoException;
27 import org.synchronoss.cpo.core.meta.domain.CpoAttribute;
28
29 /**
30 * Base {@link org.synchronoss.cpo.core.CpoData} implementation for reading a bound attribute's
31 * value out of a datastore result row (e.g. a JDBC {@code ResultSet} or Cassandra {@code Row}).
32 * Resolves the appropriate {@link MethodMapEntry} for the attribute's data type on first use and
33 * caches it, since it cannot change across invocations of the same bound instance; datastore
34 * implementations supply the actual reflective invocation via {@link #invokeGetterImpl}.
35 *
36 * @author dberry
37 */
38 public abstract class ResultSetCpoData extends AbstractBindableCpoData {
39
40 private Object rs = null;
41 MethodMapper<?> methodMapper;
42 // resolved on first use; the attribute's data type never changes, so with one instance
43 // per column the mapper lookup happens once per query instead of once per cell
44 private MethodMapEntry<?, ?> resolvedMethodMapEntry = null;
45
46 /**
47 * Creates an instance bound to a single column/attribute of the given result row.
48 *
49 * @param methodMapper the mapper used to resolve the getter method for the attribute's data type
50 * @param rs the datastore-specific result row object (e.g. {@code ResultSet} or {@code Row})
51 * @param cpoAttribute the attribute this instance reads
52 * @param index the positional index (e.g. column index) of this binding
53 */
54 public ResultSetCpoData(
55 MethodMapper<?> methodMapper, Object rs, CpoAttribute cpoAttribute, int index) {
56 super(cpoAttribute, index);
57 this.methodMapper = methodMapper;
58 this.rs = rs;
59 }
60
61 /**
62 * Gets the datastore-specific result row object this instance reads from.
63 *
64 * @return the result row object (e.g. {@code ResultSet} or {@code Row})
65 */
66 public Object getRs() {
67 return rs;
68 }
69
70 /**
71 * Invokes the datastore-specific getter method to read the raw value for the given attribute.
72 *
73 * @param cpoAttribute the attribute being read
74 * @param methodMapEntry the resolved method map entry for the attribute's data type
75 * @return the raw value read from the result row, before {@code transformIn} is applied
76 * @throws IllegalAccessException if the getter method cannot be accessed
77 * @throws InvocationTargetException if the getter method throws
78 */
79 protected abstract Object invokeGetterImpl(
80 CpoAttribute cpoAttribute, MethodMapEntry<?, ?> methodMapEntry)
81 throws IllegalAccessException, InvocationTargetException;
82
83 /**
84 * {@inheritDoc}
85 *
86 * <p>Resolves (and caches) the {@link MethodMapEntry} for the attribute's data getter return
87 * type, invokes the datastore-specific getter via {@link #invokeGetterImpl}, and applies the
88 * attribute's transform.
89 */
90 @Override
91 public Object invokeGetter() throws CpoException {
92 Object javaObject;
93 MethodMapEntry<?, ?> methodMapEntry = resolvedMethodMapEntry;
94 if (methodMapEntry == null) {
95 methodMapEntry = methodMapper.getDataMethodMapEntry(getDataGetterReturnType());
96 if (methodMapEntry == null) {
97 if (Object.class.isAssignableFrom(getDataGetterReturnType())) {
98 methodMapEntry = methodMapper.getDataMethodMapEntry(Object.class);
99 }
100 if (methodMapEntry == null) {
101 throw new CpoException(
102 "Error Retrieving Jdbc Method for type: " + getDataGetterReturnType().getName());
103 }
104 }
105 resolvedMethodMapEntry = methodMapEntry;
106 }
107
108 try {
109 // Get the getter for the Callable Statement
110 javaObject = transformIn(invokeGetterImpl(getCpoAttribute(), methodMapEntry));
111 } catch (IllegalAccessException iae) {
112 throw new CpoException("Error Invoking ResultSet Method: ", iae);
113 } catch (InvocationTargetException ite) {
114 throw new CpoException("Error Invoking ResultSet Method: ", ite);
115 }
116
117 return javaObject;
118 }
119 }