signature_builder.diff
src/com/goldencode/p2j/persist/TemporaryBuffer.java 2020-11-23 17:31:35 +0000 | ||
---|---|---|
3374 | 3374 |
|
3375 | 3375 |
if (!srcBuf.isTableDefinitelyEmpty()) |
3376 | 3376 |
{ |
3377 |
DmoMeta srcMeta = srcBuf.getDmoInfo(); |
|
3377 | 3378 |
DmoMeta dstMeta = dstBuf.getDmoInfo(); |
3378 | 3379 |
|
3379 | 3380 |
// skip validation when: |
... | ... | |
3382 | 3383 |
// TODO: enhance this if we are not in append mode, the dst and src tables are different, and they |
3383 | 3384 |
// have the same set of unique indexes (by the field position, not name) and the same set of |
3384 | 3385 |
// mandatory fields (by field position, not name) |
3385 |
boolean bulkInsert = !append && |
|
3386 |
((dstBuf.getDMOImplementationClass() == srcBuf.getDMOImplementationClass()) || |
|
3387 |
(dstMeta.getUniqueConstraints().isEmpty() && dstMeta.getMandatoryFields().isEmpty())); |
|
3386 |
boolean bulkInsert = !append && srcMeta.getSignature().equals(dstMeta.getSignature()); |
|
3387 |
|
|
3388 |
/*((dstBuf.getDMOImplementationClass() == srcBuf.getDMOImplementationClass()) || |
|
3389 |
(dstMeta.getUniqueConstraints().isEmpty() && dstMeta.getMandatoryFields().isEmpty()));*/ |
|
3388 | 3390 |
|
3389 | 3391 |
if (bulkInsert) |
3390 | 3392 |
{ |
src/com/goldencode/p2j/persist/orm/DmoMeta.java 2020-11-23 18:03:22 +0000 | ||
---|---|---|
202 | 202 |
/** The set of properties and their metadata, indexed by their in-memory positions. */ |
203 | 203 |
private Pair<Property, PropertyMeta>[] properties = null; |
204 | 204 |
|
205 |
/** The set of properties and their metadata, indexed by their in-memory positions. */ |
|
206 |
private String signature; |
|
207 |
|
|
205 | 208 |
/** The unique id of this DMO. */ |
206 | 209 |
private final int id = idSeed.incrementAndGet(); |
207 | 210 |
|
... | ... | |
311 | 314 |
fields.put(property.name, property); |
312 | 315 |
} |
313 | 316 |
|
317 |
// build a String signature suitable for identifying if two DMOs are compatible in certain |
|
318 |
// operations (for example copy-temp-table). |
|
319 |
signature = SignatureBuilder.buildSignature(getFields(false), getUniqueProperties()); |
|
320 |
|
|
314 | 321 |
// the eventual relations cannot be created at this moment, they will be processed at a |
315 | 322 |
// later time, when the respective DMO interfaces will be loaded. |
316 | 323 |
} |
... | ... | |
753 | 760 |
return uniqueConstraints; |
754 | 761 |
} |
755 | 762 |
|
763 |
public Set<Property> getUniqueProperties() |
|
764 |
{ |
|
765 |
Set<Property> uniqueProp = new HashSet<>(); |
|
766 |
List<Set<String>> indexList = getUniqueConstraints(); |
|
767 |
|
|
768 |
for (Set<String> index : indexList) |
|
769 |
{ |
|
770 |
for (String field : index) |
|
771 |
{ |
|
772 |
Property prop = fields.get(field); |
|
773 |
uniqueProp.add(prop); |
|
774 |
} |
|
775 |
} |
|
776 |
|
|
777 |
return uniqueProp; |
|
778 |
} |
|
779 |
|
|
756 | 780 |
/** |
757 | 781 |
* Get the {@code Property} that correspond to the k-th entry in {@code data} table of the class |
758 | 782 |
* implementing this DMO. |
... | ... | |
1085 | 1109 |
} |
1086 | 1110 |
|
1087 | 1111 |
/** |
1112 |
* Get the structure-based signature for this DMO. |
|
1113 |
* |
|
1114 |
* @return The signature of this DMO. |
|
1115 |
*/ |
|
1116 |
public String getSignature() |
|
1117 |
{ |
|
1118 |
return signature; |
|
1119 |
} |
|
1120 |
|
|
1121 |
/** |
|
1088 | 1122 |
* Obtain a short description of this object, usually for debugging. |
1089 | 1123 |
* |
1090 | 1124 |
* @return a short description of this object. |
src/com/goldencode/p2j/persist/orm/SignatureBuilder.java 2020-11-23 18:01:48 +0000 | ||
---|---|---|
1 |
package com.goldencode.p2j.persist.orm; |
|
2 | ||
3 |
import java.util.*; |
|
4 |
import java.util.logging.*; |
|
5 | ||
6 |
import com.goldencode.p2j.util.*; |
|
7 | ||
8 |
/** |
|
9 |
* Compute a string based signature for a DMO, which contains information regarding the |
|
10 |
* type of each field, extents and eventually some restrictions like mandatory or unique. |
|
11 |
* In order to have the prefix-code property for each generated string, the following |
|
12 |
* standards should be respected: |
|
13 |
* - The properties are represented in the order they are provided. |
|
14 |
* - A data type representation should be a single upper case character. |
|
15 |
* - In case the field has an extent, the length of the extent will follow the data type |
|
16 |
* representation as a number (no matter the number of digits) |
|
17 |
* - At the end of the representation of one property, there will be a sequence of lower-case |
|
18 |
* characters showing which restrictions are imposed on this property (mandatory/unique). The |
|
19 |
* order of these is important. |
|
20 |
*/ |
|
21 |
public class SignatureBuilder |
|
22 |
{ |
|
23 |
/** |
|
24 |
* A map statically initialized with the classic JVM data type single-char representation. |
|
25 |
* This can also contain other mappings created at the runtime. |
|
26 |
*/ |
|
27 |
private static Map<Class<? extends BaseDataType>, Character> typeMapping; |
|
28 |
|
|
29 |
/** The character used for representing a mandatory property */ |
|
30 |
private static char CHAR_MANDANTORY = 'm'; |
|
31 | ||
32 |
/** The character used for representing a unique property */ |
|
33 |
private static char CHAR_UNIQUE = 'u'; |
|
34 |
|
|
35 |
/** Logger */ |
|
36 |
private static final Logger LOG = LogHelper.getLogger(SignatureBuilder.class.getName()); |
|
37 |
|
|
38 |
/** Static initialization of the type mapping */ |
|
39 |
static |
|
40 |
{ |
|
41 |
typeMapping = new IdentityHashMap<>(); |
|
42 | ||
43 |
typeMapping.put(character.class, 'C'); |
|
44 |
typeMapping.put(decimal.class, 'F'); |
|
45 |
typeMapping.put(integer.class, 'I'); |
|
46 |
typeMapping.put(int64.class, 'J'); |
|
47 |
typeMapping.put(logical.class, 'Z'); |
|
48 |
} |
|
49 |
|
|
50 |
/** |
|
51 |
* This builds a string which structurally represents a DMO. The DMOs which are compatible, |
|
52 |
* from strcture's point of view, should have the same signature. This is useful for quickly |
|
53 |
* checking compatibility (example: copy-temp-table). |
|
54 |
* |
|
55 |
* @param it |
|
56 |
* An iterator to the whole collection of properties for the processed DMO. |
|
57 |
* @param uniqueProperties |
|
58 |
* A set of properties which are unique. These should be flagged in the signature. |
|
59 |
* @return A string representing the signature for a DMO having the specified properties.. |
|
60 |
*/ |
|
61 |
static String buildSignature(Iterator<Property> it, Set<Property> uniqueProperties) |
|
62 |
{ |
|
63 |
StringBuilder sb = new StringBuilder(); |
|
64 |
|
|
65 |
TypeIdentifierProvider findSuitableCandidate = (type) -> |
|
66 |
{ |
|
67 |
Set<Character> values = new HashSet<Character>(typeMapping.values()); |
|
68 |
for (char raw : type.getName().toCharArray()) |
|
69 |
{ |
|
70 |
char candidate = Character.toUpperCase(raw); |
|
71 |
if (!values.contains(candidate)) |
|
72 |
{ |
|
73 |
typeMapping.put(type, candidate); |
|
74 |
return candidate; |
|
75 |
} |
|
76 |
} |
|
77 |
return null; |
|
78 |
}; |
|
79 |
|
|
80 |
TypeIdentifierProvider findAnyCandidate = (type) -> |
|
81 |
{ |
|
82 |
Set<Character> values = new HashSet<Character>(typeMapping.values()); |
|
83 |
for (char candidate = 'A'; candidate <= 'Z'; candidate++) |
|
84 |
{ |
|
85 |
if (!values.contains(candidate)) |
|
86 |
{ |
|
87 |
typeMapping.put(type, candidate); |
|
88 |
return candidate; |
|
89 |
} |
|
90 |
} |
|
91 |
return null; |
|
92 |
}; |
|
93 |
|
|
94 |
while (it.hasNext()) |
|
95 |
{ |
|
96 |
Property prop = it.next(); |
|
97 |
Class<? extends BaseDataType> type = (Class<? extends BaseDataType>) prop._fwdType; |
|
98 |
Character ch = typeMapping.get(type); |
|
99 |
if (ch == null) |
|
100 |
{ |
|
101 |
// for this kind of situations, find a suitable representation for this type |
|
102 |
ch = findSuitableCandidate.provide(type); |
|
103 |
} |
|
104 |
if (ch == null) |
|
105 |
{ |
|
106 |
// can't find a suitable representation, assign a random character |
|
107 |
ch = findAnyCandidate.provide(type); |
|
108 |
} |
|
109 |
if (ch == null) |
|
110 |
{ |
|
111 |
if (LOG.isLoggable(Level.WARNING)) |
|
112 |
{ |
|
113 |
LOG.log(Level.WARNING, "Couldn't find a character representation for " + type.getName()); |
|
114 |
} |
|
115 |
return null; |
|
116 |
} |
|
117 |
|
|
118 |
sb.append(ch); |
|
119 |
|
|
120 |
int extent = prop.extent; |
|
121 |
if (extent > 0) |
|
122 |
{ |
|
123 |
sb.append(extent); |
|
124 |
} |
|
125 |
|
|
126 |
processFieldRestrictions(prop, uniqueProperties.contains(prop), sb); |
|
127 |
} |
|
128 |
|
|
129 |
return sb.toString(); |
|
130 |
} |
|
131 |
|
|
132 |
/** |
|
133 |
* Helper which sets restrictions like mandatory or unique to a specified field. |
|
134 |
* |
|
135 |
* @param prop |
|
136 |
* The property for which the restriction analysis is done. |
|
137 |
* @param unique |
|
138 |
* A flag which indicates that the property should be unique. |
|
139 |
* @param sb |
|
140 |
* The string builder which should be completed with the found restrictions. |
|
141 |
*/ |
|
142 |
private static void processFieldRestrictions(Property prop, |
|
143 |
boolean unique, |
|
144 |
StringBuilder sb) |
|
145 |
{ |
|
146 |
if (prop.mandatory) |
|
147 |
{ |
|
148 |
sb.append(CHAR_MANDANTORY); |
|
149 |
} |
|
150 |
|
|
151 |
if (unique) |
|
152 |
{ |
|
153 |
sb.append(CHAR_UNIQUE); |
|
154 |
} |
|
155 |
} |
|
156 |
|
|
157 |
/** |
|
158 |
* Helper for anonymous functions used in identifying a suitable character for a specified data type. |
|
159 |
*/ |
|
160 |
interface TypeIdentifierProvider |
|
161 |
{ |
|
162 |
Character provide(Class<? extends BaseDataType> type); |
|
163 |
} |
|
164 |
} |