-
Notifications
You must be signed in to change notification settings - Fork 188
/
SqaleTransformerBase.java
220 lines (193 loc) · 8.59 KB
/
SqaleTransformerBase.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
/*
* Copyright (C) 2010-2021 Evolveum and contributors
*
* This work is dual-licensed under the Apache License 2.0
* and European Union Public License. See LICENSE file for details.
*/
package com.evolveum.midpoint.repo.sqale.qmodel;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
import java.util.function.Consumer;
import javax.xml.namespace.QName;
import com.querydsl.core.Tuple;
import com.querydsl.sql.ColumnMetadata;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.evolveum.midpoint.repo.sqale.SqaleTransformerSupport;
import com.evolveum.midpoint.repo.sqale.qmodel.common.QUri;
import com.evolveum.midpoint.repo.sqale.qmodel.object.MObjectType;
import com.evolveum.midpoint.repo.sqale.qmodel.ref.MReference;
import com.evolveum.midpoint.repo.sqale.qmodel.ref.QReferenceMapping;
import com.evolveum.midpoint.repo.sqale.qmodel.ref.ReferenceSqlTransformer;
import com.evolveum.midpoint.repo.sqlbase.JdbcSession;
import com.evolveum.midpoint.repo.sqlbase.SqlTransformerSupport;
import com.evolveum.midpoint.repo.sqlbase.mapping.QueryTableMapping;
import com.evolveum.midpoint.repo.sqlbase.mapping.SqlTransformer;
import com.evolveum.midpoint.repo.sqlbase.querydsl.FlexibleRelationalPathBase;
import com.evolveum.midpoint.schema.GetOperationOptions;
import com.evolveum.midpoint.schema.SelectorOptions;
import com.evolveum.midpoint.util.MiscUtil;
import com.evolveum.midpoint.util.QNameUtil;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType;
import com.evolveum.prism.xml.ns._public.types_3.PolyStringType;
/**
* @param <S> schema type
* @param <Q> type of entity path
* @param <R> type of the row bean for the table
*/
public abstract class SqaleTransformerBase<S, Q extends FlexibleRelationalPathBase<R>, R>
implements SqlTransformer<S, Q, R> {
protected final Logger logger = LoggerFactory.getLogger(getClass());
protected final SqaleTransformerSupport transformerSupport;
/**
* Constructor uses {@link SqlTransformerSupport} type even when it really is
* {@link SqaleTransformerSupport}, but this way we can cast it just once here; otherwise cast
* would be needed in each implementation of {@link QueryTableMapping#createTransformer)}.
* (Alternative is to parametrize {@link QueryTableMapping} with various {@link SqlTransformer}
* types which is not convenient at all. This little downcast is low price to pay.)
*/
protected SqaleTransformerBase(SqlTransformerSupport transformerSupport) {
this.transformerSupport = (SqaleTransformerSupport) transformerSupport;
}
protected abstract QueryTableMapping<S, Q, R> mapping();
@Override
public S toSchemaObject(R row) {
throw new UnsupportedOperationException("Use toSchemaObject(Tuple,...)");
}
/**
* Transforms row Tuple containing {@link R} under entity path and extension columns.
*/
@Override
public S toSchemaObject(Tuple tuple, Q entityPath,
Collection<SelectorOptions<GetOperationOptions>> options)
throws SchemaException {
S schemaObject = toSchemaObject(tuple.get(entityPath));
processExtensionColumns(schemaObject, tuple, entityPath);
return schemaObject;
}
@SuppressWarnings("unused")
protected void processExtensionColumns(S schemaObject, Tuple tuple, Q entityPath) {
// empty by default, can be overridden
}
/**
* Returns {@link ObjectReferenceType} with specified oid, proper type based on
* {@link MObjectType} and, optionally, target name/description.
* Returns {@code null} if OID is null.
* Fails if OID is not null and {@code repoObjectType} is null.
*/
@Nullable
protected ObjectReferenceType objectReferenceType(
@Nullable String oid, MObjectType repoObjectType, String targetName) {
if (oid == null) {
return null;
}
if (repoObjectType == null) {
throw new IllegalArgumentException(
"NULL object type provided for object reference with OID " + oid);
}
return new ObjectReferenceType()
.oid(oid)
.type(transformerSupport.schemaClassToQName(repoObjectType.getSchemaType()))
.description(targetName)
.targetName(targetName);
}
/**
* Trimming the value to the column size from column metadata (must be specified).
*/
protected @Nullable String trim(
@Nullable String value, @NotNull ColumnMetadata columnMetadata) {
if (!columnMetadata.hasSize()) {
throw new IllegalArgumentException(
"trimString with column metadata without specified size: " + columnMetadata);
}
return MiscUtil.trimString(value, columnMetadata.getSize());
}
protected @NotNull Integer searchCachedRelationId(QName qName) {
return transformerSupport.searchCachedRelationId(qName);
}
/** Returns ID for cached URI without going to the database. */
protected Integer resolveUriToId(String uri) {
return transformerSupport.resolveUriToId(uri);
}
/**
* Returns ID for relation QName creating new {@link QUri} row in DB as needed.
* Relation is normalized before consulting the cache.
* Never returns null, returns default ID for configured default relation.
*/
protected Integer processCacheableRelation(QName qName) {
return transformerSupport.processCacheableRelation(qName);
}
/** Returns ID for URI creating new cache row in DB as needed. */
protected Integer processCacheableUri(String uri) {
return uri != null
? transformerSupport.processCacheableUri(uri)
: null;
}
/** Returns ID for URI creating new cache row in DB as needed. */
protected Integer processCacheableUri(QName qName) {
return qName != null
? transformerSupport.processCacheableUri(QNameUtil.qNameToUri(qName))
: null;
}
/**
* Returns IDs as Integer array for URI strings creating new cache row in DB as needed.
* Returns null for null or empty list on input.
*/
protected Integer[] processCacheableUris(List<String> uris) {
if (uris == null || uris.isEmpty()) {
return null;
}
return uris.stream()
.map(uri -> processCacheableUri(uri))
.toArray(Integer[]::new);
}
protected @Nullable UUID oidToUUid(@Nullable String oid) {
return oid != null ? UUID.fromString(oid) : null;
}
protected MObjectType schemaTypeToObjectType(QName schemaType) {
return schemaType == null ? null :
MObjectType.fromSchemaType(
transformerSupport.qNameToSchemaClass(schemaType));
}
protected void setPolyString(PolyStringType polyString,
Consumer<String> origConsumer, Consumer<String> normConsumer) {
if (polyString != null) {
origConsumer.accept(polyString.getOrig());
normConsumer.accept(polyString.getNorm());
}
}
protected void setReference(ObjectReferenceType ref,
Consumer<UUID> targetOidConsumer, Consumer<MObjectType> targetTypeConsumer,
Consumer<Integer> relationIdConsumer) {
if (ref != null) {
targetOidConsumer.accept(oidToUUid(ref.getOid()));
targetTypeConsumer.accept(schemaTypeToObjectType(ref.getType()));
relationIdConsumer.accept(processCacheableRelation(ref.getRelation()));
}
}
protected <REF extends MReference, OQ extends FlexibleRelationalPathBase<OR>, OR> void storeRefs(
@NotNull OR ownerRow, @NotNull List<ObjectReferenceType> refs,
@NotNull QReferenceMapping<?, REF, OQ, OR> mapping, @NotNull JdbcSession jdbcSession) {
if (!refs.isEmpty()) {
ReferenceSqlTransformer<?, REF, OR> transformer =
mapping.createTransformer(transformerSupport);
refs.forEach(ref -> transformer.insert(ref, ownerRow, jdbcSession));
}
}
protected String[] arrayFor(List<String> strings) {
if (strings == null || strings.isEmpty()) {
return null;
}
return strings.toArray(String[]::new);
}
/** Convenient insert shortcut when the row is fully populated. */
protected void insert(R row, JdbcSession jdbcSession) {
jdbcSession.newInsert(mapping().defaultAlias())
.populate(row)
.execute();
}
}