Skip to content

Commit 74006d9

Browse files
committed
Add objecthandling since it is required by unit testing module
1 parent 7b2a4b2 commit 74006d9

24 files changed

+2291
-0
lines changed

Audit trail.mpr

0 Bytes
Binary file not shown.
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package objecthandling;
2+
3+
import org.apache.commons.lang3.builder.HashCodeBuilder;
4+
5+
6+
public class ImmutablePair<T, U>
7+
{
8+
public static <T, U> ImmutablePair<T, U> of(T left, U right) {
9+
return new ImmutablePair<T, U>(left, right);
10+
}
11+
12+
private final T left;
13+
private final U right;
14+
15+
private ImmutablePair(T left, U right) {
16+
if (left == null)
17+
throw new IllegalArgumentException("Left is NULL");
18+
if (right == null)
19+
throw new IllegalArgumentException("Right is NULL");
20+
21+
this.left = left;
22+
this.right = right;
23+
}
24+
25+
public T getLeft() {
26+
return left;
27+
}
28+
29+
public U getRight() {
30+
return right;
31+
}
32+
33+
@Override
34+
public String toString() {
35+
return "<" + left.toString()+ "," + right.toString() + ">";
36+
}
37+
38+
@Override
39+
public boolean equals(Object other) {
40+
if (!(other instanceof ImmutablePair<?,?>))
41+
return false;
42+
43+
if (this == other)
44+
return true;
45+
46+
ImmutablePair<?,?> o = (ImmutablePair<?, ?>) other;
47+
return left.equals(o.getLeft()) && right.equals(o.getRight());
48+
}
49+
50+
@Override
51+
public int hashCode() {
52+
return new HashCodeBuilder(19, 85)
53+
.append(left)
54+
.append(right)
55+
.toHashCode();
56+
}
57+
58+
}

javasource/objecthandling/ORM.java

Lines changed: 331 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,331 @@
1+
package objecthandling;
2+
3+
import java.text.SimpleDateFormat;
4+
import java.util.ArrayList;
5+
import java.util.Arrays;
6+
import java.util.Date;
7+
import java.util.HashMap;
8+
import java.util.List;
9+
import java.util.Map;
10+
11+
import com.mendix.core.Core;
12+
import com.mendix.core.CoreException;
13+
import com.mendix.core.objectmanagement.member.MendixAutoNumber;
14+
import com.mendix.core.objectmanagement.member.MendixDateTime;
15+
import com.mendix.core.objectmanagement.member.MendixEnum;
16+
import com.mendix.core.objectmanagement.member.MendixObjectReference;
17+
import com.mendix.core.objectmanagement.member.MendixObjectReferenceSet;
18+
import com.mendix.systemwideinterfaces.core.IContext;
19+
import com.mendix.systemwideinterfaces.core.IMendixIdentifier;
20+
import com.mendix.systemwideinterfaces.core.IMendixObject;
21+
import com.mendix.systemwideinterfaces.core.IMendixObject.ObjectState;
22+
import com.mendix.systemwideinterfaces.core.IMendixObjectMember;
23+
import com.mendix.systemwideinterfaces.core.IMendixObjectMember.MemberState;
24+
import com.mendix.systemwideinterfaces.core.meta.IMetaAssociation;
25+
import com.mendix.systemwideinterfaces.core.meta.IMetaAssociation.AssociationType;
26+
import com.mendix.systemwideinterfaces.core.meta.IMetaEnumValue;
27+
import com.mendix.systemwideinterfaces.core.meta.IMetaEnumeration;
28+
import com.mendix.systemwideinterfaces.core.meta.IMetaObject;
29+
import com.mendix.systemwideinterfaces.core.meta.IMetaPrimitive;
30+
import com.mendix.systemwideinterfaces.core.meta.IMetaPrimitive.PrimitiveType;
31+
32+
public class ORM
33+
{
34+
35+
public static Long getGUID(IMendixObject item)
36+
{
37+
return item.getId().toLong();
38+
}
39+
40+
public static String getOriginalValueAsString(IContext context, IMendixObject item,
41+
String member)
42+
{
43+
return String.valueOf(item.getMember(context, member).getOriginalValue(context));
44+
}
45+
46+
public static boolean objectHasChanged(IMendixObject anyobject) {
47+
if (anyobject == null)
48+
throw new IllegalArgumentException("The provided object is empty");
49+
return anyobject.isChanged();
50+
}
51+
52+
/**
53+
* checks whether a certain member of an object has changed. If the objects itself is still new, we consider to be changes as well.
54+
* @param item
55+
* @param member
56+
* @param context
57+
* @return
58+
*/
59+
public static boolean memberHasChanged(IContext context, IMendixObject item, String member)
60+
{
61+
if (item == null)
62+
throw new IllegalArgumentException("The provided object is empty");
63+
if (!item.hasMember(member))
64+
throw new IllegalArgumentException("Unknown member: " + member);
65+
return item.getMember(context, member).getState() == MemberState.CHANGED || item.getState() != ObjectState.NORMAL;
66+
}
67+
68+
public static void deepClone(IContext c, IMendixObject source, IMendixObject target, String membersToSkip, String membersToKeep, String reverseAssociations, String excludeEntities, String excludeModules) throws CoreException
69+
{
70+
List<String> toskip = Arrays.asList((membersToSkip + ",createdDate,changedDate").split(","));
71+
List<String> tokeep = Arrays.asList((membersToKeep + ",System.owner,System.changedBy").split(","));
72+
List<String> revAssoc = Arrays.asList(reverseAssociations.split(","));
73+
List<String> skipEntities = Arrays.asList(excludeEntities.split(","));
74+
List<String> skipModules = Arrays.asList(excludeModules.split(","));
75+
Map<IMendixIdentifier, IMendixIdentifier> mappedIDs = new HashMap<IMendixIdentifier, IMendixIdentifier>();
76+
duplicate(c, source, target, toskip, tokeep, revAssoc, skipEntities, skipModules, mappedIDs);
77+
}
78+
79+
private static void duplicate(IContext ctx, IMendixObject src, IMendixObject tar,
80+
List<String> toskip, List<String> tokeep, List<String> revAssoc,
81+
List<String> skipEntities, List<String> skipModules,
82+
Map<IMendixIdentifier, IMendixIdentifier> mappedObjects) throws CoreException
83+
{
84+
mappedObjects.put(src.getId(), tar.getId());
85+
86+
Map<String, ? extends IMendixObjectMember<?>> members = src.getMembers(ctx);
87+
String type = src.getType() + "/";
88+
89+
for(String key : members.keySet())
90+
if (!toskip.contains(key) && !toskip.contains(type + key)){
91+
IMendixObjectMember<?> m = members.get(key);
92+
if (m.isVirtual() || m instanceof MendixAutoNumber)
93+
continue;
94+
95+
boolean keep = tokeep.contains(key) || tokeep.contains(type + key);
96+
97+
if (m instanceof MendixObjectReference && !keep && m.getValue(ctx) != null) {
98+
IMendixObject o = Core.retrieveId(ctx, ((MendixObjectReference) m).getValue(ctx));
99+
IMendixIdentifier refObj = getCloneOfObject(ctx, o, toskip, tokeep, revAssoc, skipEntities, skipModules, mappedObjects);
100+
tar.setValue(ctx, key, refObj);
101+
}
102+
103+
else if (m instanceof MendixObjectReferenceSet && !keep && m.getValue(ctx) != null) {
104+
MendixObjectReferenceSet rs = (MendixObjectReferenceSet) m;
105+
List<IMendixIdentifier> res = new ArrayList<IMendixIdentifier>();
106+
for(IMendixIdentifier item : rs.getValue(ctx)) {
107+
IMendixObject o = Core.retrieveId(ctx, item);
108+
IMendixIdentifier refObj = getCloneOfObject(ctx, o, toskip, tokeep, revAssoc, skipEntities, skipModules, mappedObjects);
109+
res.add(refObj);
110+
}
111+
tar.setValue(ctx, key, res);
112+
}
113+
114+
else if (m instanceof MendixAutoNumber) //skip autonumbers! Ticket 14893
115+
continue;
116+
117+
else {
118+
tar.setValue(ctx, key, m.getValue(ctx));
119+
}
120+
}
121+
Core.commitWithoutEvents(ctx, tar);
122+
duplicateReverseAssociations(ctx, src, tar, toskip, tokeep, revAssoc, skipEntities, skipModules, mappedObjects);
123+
}
124+
125+
private static IMendixIdentifier getCloneOfObject(IContext ctx, IMendixObject src,
126+
List<String> toskip, List<String> tokeep, List<String> revAssoc,
127+
List<String> skipEntities, List<String> skipModules,
128+
Map<IMendixIdentifier, IMendixIdentifier> mappedObjects) throws CoreException
129+
{
130+
String objType = src.getMetaObject().getName();
131+
String modName = src.getMetaObject().getModuleName();
132+
133+
// if object is already being cloned, return ref to clone
134+
if (mappedObjects.containsKey(src.getId())) {
135+
return mappedObjects.get(src.getId());
136+
// if object should be skipped based on module or entity, return source object
137+
} else if (skipEntities.contains(objType) || skipModules.contains(modName)) {
138+
return src.getId();
139+
// if not already being cloned, create clone
140+
} else {
141+
IMendixObject clone = Core.instantiate(ctx, src.getType());
142+
duplicate(ctx, src, clone, toskip, tokeep, revAssoc, skipEntities, skipModules, mappedObjects);
143+
return clone.getId();
144+
}
145+
}
146+
147+
private static void duplicateReverseAssociations(IContext ctx, IMendixObject src, IMendixObject tar,
148+
List<String> toskip, List<String> tokeep, List<String> revAssocs,
149+
List<String> skipEntities, List<String> skipModules,
150+
Map<IMendixIdentifier, IMendixIdentifier> mappedObjects) throws CoreException
151+
{
152+
for(String fullAssocName : revAssocs) {
153+
String[] parts = fullAssocName.split("/");
154+
155+
if (parts.length != 1 && parts.length != 3) //specifying entity has no meaning anymore, but remain backward compatible.
156+
throw new IllegalArgumentException("Reverse association is not defined correctly, please mention the relation name only: '" + fullAssocName + "'");
157+
158+
String assocname = parts.length == 3 ? parts[1] : parts[0]; //support length 3 for backward compatibility
159+
160+
IMetaAssociation massoc = src.getMetaObject().getDeclaredMetaAssociationChild(assocname);
161+
162+
if (massoc != null) {
163+
IMetaObject relationParent = massoc.getParent();
164+
// if the parent is in the exclude list, we can't clone the parent, and setting the
165+
// references to the newly cloned target object will screw up the source data.
166+
if (skipEntities.contains(relationParent.getName()) || skipModules.contains(relationParent.getModuleName())){
167+
throw new IllegalArgumentException("A reverse reference has been specified that starts at an entity in the exclude list, this is not possible to clone: '" + fullAssocName + "'");
168+
}
169+
170+
//MWE: what to do with reverse reference sets? -> to avoid spam creating objects on
171+
//reverse references, do not support referenceset (todo: we could keep a map of converted guids and reuse that!)
172+
if (massoc.getType() == AssociationType.REFERENCESET) {
173+
throw new IllegalArgumentException("It is not possible to clone reverse referencesets: '" + fullAssocName + "'");
174+
}
175+
176+
List<IMendixObject> objs = Core.retrieveXPathQuery(ctx, String.format("//%s[%s='%s']",
177+
relationParent.getName(), assocname, String.valueOf(src.getId().toLong())));
178+
179+
for(IMendixObject obj : objs) {
180+
@SuppressWarnings("unused") // object is unused on purpose
181+
IMendixIdentifier refObj = getCloneOfObject(ctx, obj, toskip, tokeep, revAssocs, skipEntities, skipModules, mappedObjects);
182+
// setting reference explicitly is not necessary, this has been done in the
183+
// duplicate() call.
184+
}
185+
}
186+
}
187+
}
188+
189+
public static Boolean commitWithoutEvents(IContext context, IMendixObject subject) throws CoreException
190+
{
191+
Core.commitWithoutEvents(context, subject);
192+
return true;
193+
}
194+
195+
public static String getValueOfPath(IContext context, IMendixObject substitute, String fullpath, String datetimeformat) throws Exception
196+
{
197+
String[] path = fullpath.split("/");
198+
if (path.length == 1) {
199+
IMendixObjectMember<?> member = substitute.getMember(context, path[0]);
200+
201+
//special case, see ticket 9135, format datetime.
202+
if (member instanceof MendixDateTime) {
203+
Date time = ((MendixDateTime) member).getValue(context);
204+
if (time == null)
205+
return "";
206+
String f = datetimeformat != null && !datetimeformat.isEmpty() ? datetimeformat : "EEE dd MMM yyyy, HH:mm";
207+
return new SimpleDateFormat(f).format(time);
208+
}
209+
210+
if (member instanceof MendixEnum) {
211+
String value = member.parseValueToString(context);
212+
if (value == null || value.isEmpty())
213+
return "";
214+
215+
IMetaEnumeration enumeration = ((MendixEnum)member).getEnumeration();
216+
IMetaEnumValue evalue = enumeration.getEnumValues().get(value);
217+
return Core.getInternationalizedString(context, evalue.getI18NCaptionKey());
218+
}
219+
//default
220+
return member.parseValueToString(context);
221+
}
222+
223+
else if (path.length == 0)
224+
throw new Exception("communitycommons.ORM.getValueOfPath: Unexpected end of path.");
225+
226+
else {
227+
IMendixObjectMember<?> member = substitute.getMember(context, path[0]);
228+
if (member instanceof MendixObjectReference) {
229+
MendixObjectReference ref = (MendixObjectReference) member;
230+
IMendixIdentifier id = ref.getValue(context);
231+
if (id == null)
232+
return "";
233+
IMendixObject obj = Core.retrieveId(context, id);
234+
if (obj == null)
235+
return "";
236+
return getValueOfPath(context, obj, fullpath.substring(fullpath.indexOf("/") + 1), datetimeformat);
237+
}
238+
239+
else if (member instanceof MendixObjectReferenceSet) {
240+
MendixObjectReferenceSet ref = (MendixObjectReferenceSet) member;
241+
List<IMendixIdentifier> ids = ref.getValue(context);
242+
if (ids == null)
243+
return "";
244+
StringBuilder res = new StringBuilder();
245+
for(IMendixIdentifier id : ids) {
246+
if (id == null)
247+
continue;
248+
IMendixObject obj = Core.retrieveId(context, id);
249+
if (obj == null)
250+
continue;
251+
res.append(", ");
252+
res.append(getValueOfPath(context, obj, fullpath.substring(fullpath.indexOf("/") + 1), datetimeformat));
253+
}
254+
return res.length() > 1 ? res.toString().substring(2) : "";
255+
}
256+
else throw new Exception("communitycommons.ORM.getValueOfPath: Not a valid reference: '"+path[0]+"' in '"+ fullpath +"'");
257+
}
258+
}
259+
260+
public static Boolean cloneObject(IContext c, IMendixObject source,
261+
IMendixObject target, Boolean withAssociations)
262+
{
263+
Map<String, ? extends IMendixObjectMember<?>> members = source.getMembers(c);
264+
265+
for(String key : members.keySet()) {
266+
IMendixObjectMember<?> m = members.get(key);
267+
if (m.isVirtual())
268+
continue;
269+
if (m instanceof MendixAutoNumber)
270+
continue;
271+
if (withAssociations || ((!(m instanceof MendixObjectReference) && !(m instanceof MendixObjectReferenceSet)&& !(m instanceof MendixAutoNumber))))
272+
target.setValue(c, key, m.getValue(c));
273+
}
274+
return true;
275+
}
276+
277+
public static IMendixObject getLastChangedByUser(IContext context,
278+
IMendixObject thing) throws CoreException
279+
{
280+
if (thing == null || !thing.hasChangedByAttribute())
281+
return null;
282+
283+
IMendixIdentifier itemId = thing.getChangedBy(context);
284+
if (itemId == null)
285+
return null;
286+
287+
return Core.retrieveId(context, itemId);
288+
}
289+
290+
public static IMendixObject getCreatedByUser(IContext context,
291+
IMendixObject thing) throws CoreException
292+
{
293+
if (thing == null || !thing.hasOwnerAttribute())
294+
return null;
295+
296+
IMendixIdentifier itemId = thing.getOwner(context);
297+
if (itemId == null)
298+
return null;
299+
300+
return Core.retrieveId(context, itemId);
301+
}
302+
303+
public static void commitSilent(IContext c, IMendixObject mendixObject)
304+
{
305+
try
306+
{
307+
Core.commit(c, mendixObject);
308+
}
309+
catch (CoreException e)
310+
{
311+
throw new RuntimeException(e);
312+
}
313+
}
314+
315+
public static void copyAttributes(IContext context, IMendixObject source, IMendixObject target)
316+
{
317+
if (source == null)
318+
throw new IllegalStateException("source is null");
319+
if (target == null)
320+
throw new IllegalStateException("target is null");
321+
322+
for(IMetaPrimitive e : target.getMetaObject().getMetaPrimitives()) {
323+
if (!source.hasMember(e.getName()))
324+
continue;
325+
if (e.isVirtual() || e.getType() == PrimitiveType.AutoNumber)
326+
continue;
327+
328+
target.setValue(context, e.getName(), source.getValue(context, e.getName()));
329+
}
330+
}
331+
}

0 commit comments

Comments
 (0)