1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.mycore.datamodel.classifications2.impl;
20
21 import java.net.URI;
22 import java.util.AbstractMap;
23 import java.util.ArrayList;
24 import java.util.Collection;
25 import java.util.HashMap;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Optional;
29 import java.util.SortedSet;
30 import java.util.function.BiConsumer;
31 import java.util.function.Consumer;
32 import java.util.function.Function;
33 import java.util.stream.Collector;
34 import java.util.stream.Collectors;
35
36 import org.apache.logging.log4j.LogManager;
37 import org.apache.logging.log4j.Logger;
38 import org.mycore.backend.jpa.MCREntityManagerProvider;
39 import org.mycore.common.MCRException;
40 import org.mycore.common.MCRPersistenceException;
41 import org.mycore.common.MCRStreamUtils;
42 import org.mycore.datamodel.classifications2.MCRCategory;
43 import org.mycore.datamodel.classifications2.MCRCategoryDAO;
44 import org.mycore.datamodel.classifications2.MCRCategoryID;
45 import org.mycore.datamodel.classifications2.MCRLabel;
46
47 import jakarta.persistence.EntityManager;
48 import jakarta.persistence.EntityNotFoundException;
49 import jakarta.persistence.FlushModeType;
50 import jakarta.persistence.NoResultException;
51 import jakarta.persistence.Query;
52 import jakarta.persistence.TypedQuery;
53
54
55
56
57
58
59
60
61
62 public class MCRCategoryDAOImpl implements MCRCategoryDAO {
63
64 private static final int LEVEL_START_VALUE = 0;
65
66 private static final int LEFT_START_VALUE = 0;
67
68 private static long LAST_MODIFIED = System.currentTimeMillis();
69
70 private static final Logger LOGGER = LogManager.getLogger();
71
72 private static final String NAMED_QUERY_NAMESPACE = "MCRCategory.";
73
74 private static HashMap<String, Long> LAST_MODIFIED_MAP = new HashMap<>();
75
76 @Override
77 public MCRCategory addCategory(MCRCategoryID parentID, MCRCategory category) {
78 int position = -1;
79 if (category instanceof MCRCategoryImpl) {
80 position = ((MCRCategoryImpl) category).getPositionInParent();
81 }
82 return addCategory(parentID, category, position);
83 }
84
85 @Override
86 public MCRCategory addCategory(MCRCategoryID parentID, MCRCategory category, int position) {
87 if (exist(category.getId())) {
88 throw new MCRException("Cannot add category. A category with ID " + category.getId() + " already exists");
89 }
90 return withoutFlush(MCREntityManagerProvider.getCurrentEntityManager(), false, entityManager -> {
91
92 entityManager.flush();
93 entityManager.clear();
94 int leftStart = LEFT_START_VALUE;
95 int levelStart = LEVEL_START_VALUE;
96 MCRCategoryImpl parent = null;
97 if (parentID != null) {
98 parent = getByNaturalID(entityManager, parentID);
99 levelStart = parent.getLevel() + 1;
100 leftStart = parent.getRight();
101 if (position > parent.getChildren().size()) {
102 throw new IndexOutOfBoundsException(
103 "Cannot add category as child #" + position + ", when there are only "
104 + parent.getChildren().size() + " children.");
105 }
106 }
107 LOGGER.debug("Calculating LEFT,RIGHT and LEVEL attributes...");
108 final MCRCategoryImpl wrapCategory = MCRCategoryImpl.wrapCategory(category, parent,
109 parent == null ? category.getRoot() : parent.getRoot());
110 wrapCategory.calculateLeftRightAndLevel(leftStart, levelStart);
111
112 int nodes = 1 + (wrapCategory.getRight() - wrapCategory.getLeft()) / 2;
113 LOGGER.debug("Calculating LEFT,RIGHT and LEVEL attributes. Done! Nodes: {}", nodes);
114 if (parentID != null) {
115 final int increment = nodes * 2;
116 int parentLeft = parent.getLeft();
117 updateLeftRightValue(entityManager, parentID.getRootID(), leftStart, increment);
118 entityManager.flush();
119 if (position < 0) {
120 parent.getChildren().add(category);
121 } else {
122 parent.getChildren().add(position, category);
123 }
124 parent.calculateLeftRightAndLevel(Integer.MAX_VALUE / 2, parent.getLevel());
125 entityManager.flush();
126 parent.calculateLeftRightAndLevel(parentLeft, parent.getLevel());
127 }
128 entityManager.persist(category);
129 LOGGER.info("Category {} saved.", category.getId());
130 updateTimeStamp();
131
132 updateLastModified(category.getRoot().getId().toString());
133 return parent;
134 });
135 }
136
137 @Override
138 public void deleteCategory(MCRCategoryID id) {
139 EntityManager entityManager = MCREntityManagerProvider.getCurrentEntityManager();
140 LOGGER.debug("Will get: {}", id);
141 MCRCategoryImpl category = getByNaturalID(entityManager, id);
142 try {
143 entityManager.refresh(category);
144 } catch (EntityNotFoundException e) {
145
146
147 }
148 if (category == null) {
149 throw new MCRPersistenceException("Category " + id + " was not found. Delete aborted.");
150 }
151 LOGGER.debug("Will delete: {}", category.getId());
152 MCRCategory parent = category.parent;
153 category.detachFromParent();
154 entityManager.remove(category);
155 if (parent != null) {
156 entityManager.flush();
157 LOGGER.debug("Left: {} Right: {}", category.getLeft(), category.getRight());
158
159 int nodes = 1 + (category.getRight() - category.getLeft()) / 2;
160 final int increment = nodes * -2;
161
162 updateLeftRightValue(entityManager, category.getRootID(), category.getLeft(), increment);
163 }
164 updateTimeStamp();
165 updateLastModified(category.getRootID());
166 }
167
168
169
170
171
172
173 @Override
174 public boolean exist(MCRCategoryID id) {
175 return getLeftRightLevelValues(MCREntityManagerProvider.getCurrentEntityManager(), id) != null;
176 }
177
178 @Override
179 public List<MCRCategory> getCategoriesByLabel(final String lang, final String text) {
180 EntityManager entityManager = MCREntityManagerProvider.getCurrentEntityManager();
181 return cast(entityManager.createNamedQuery(NAMED_QUERY_NAMESPACE + "byLabel", MCRCategoryImpl.class)
182 .setParameter("lang", lang)
183 .setParameter("text", text)
184 .getResultList());
185 }
186
187 @Override
188 public List<MCRCategory> getCategoriesByLabel(MCRCategoryID baseID, String lang, String text) {
189 EntityManager entityManager = MCREntityManagerProvider.getCurrentEntityManager();
190 MCRCategoryDTO leftRight = getLeftRightLevelValues(entityManager, baseID);
191 return cast(entityManager
192 .createNamedQuery(NAMED_QUERY_NAMESPACE + "byLabelInClass", MCRCategoryImpl.class)
193 .setParameter("rootID", baseID.getRootID())
194 .setParameter("left", leftRight.leftValue)
195 .setParameter("right", leftRight.rightValue)
196 .setParameter("lang", lang)
197 .setParameter("text", text)
198 .getResultList());
199 }
200
201 @Override
202 @SuppressWarnings("unchecked")
203 public MCRCategory getCategory(MCRCategoryID id, int childLevel) {
204 EntityManager entityManager = MCREntityManagerProvider.getCurrentEntityManager();
205 final boolean fetchAllChildren = childLevel < 0;
206 Query q;
207 if (id.isRootID()) {
208 q = entityManager.createNamedQuery(NAMED_QUERY_NAMESPACE
209 + (fetchAllChildren ? "prefetchClassQuery" : "prefetchClassLevelQuery"));
210 if (!fetchAllChildren) {
211 q.setParameter("endlevel", childLevel);
212 }
213 q.setParameter("classID", id.getRootID());
214 } else {
215
216 MCRCategoryDTO leftRightLevel = getLeftRightLevelValues(entityManager, id);
217 if (leftRightLevel == null) {
218 return null;
219 }
220 q = entityManager.createNamedQuery(NAMED_QUERY_NAMESPACE
221 + (fetchAllChildren ? "prefetchCategQuery" : "prefetchCategLevelQuery"));
222 if (!fetchAllChildren) {
223 q.setParameter("endlevel", leftRightLevel.level + childLevel);
224 }
225 q.setParameter("classID", id.getRootID());
226 q.setParameter("left", leftRightLevel.leftValue);
227 q.setParameter("right", leftRightLevel.rightValue);
228 }
229 List<MCRCategoryDTO> result = q.getResultList();
230 if (result.isEmpty()) {
231 LOGGER.warn("Could not load category: {}", id);
232 return null;
233 }
234 return buildCategoryFromPrefetchedList(result, id);
235 }
236
237
238
239
240
241
242 @Override
243 public List<MCRCategory> getChildren(MCRCategoryID cid) {
244 LOGGER.debug("Get children of category: {}", cid);
245 return Optional.ofNullable(cid)
246 .map(id -> getCategory(id, 1))
247 .map(MCRCategory::getChildren)
248 .map(l -> l
249 .parallelStream()
250 .collect(Collectors.toList())
251 .parallelStream()
252 .map(MCRCategoryImpl.class::cast)
253 .peek(MCRCategoryImpl::detachFromParent)
254 .map(MCRCategory.class::cast)
255 .collect(Collectors.toList()))
256 .orElse(new MCRCategoryChildList(null, null));
257 }
258
259 @Override
260 public List<MCRCategory> getParents(MCRCategoryID id) {
261 EntityManager entityManager = MCREntityManagerProvider.getCurrentEntityManager();
262 MCRCategoryDTO leftRight = getLeftRightLevelValues(entityManager, id);
263 if (leftRight == null) {
264 return null;
265 }
266 Query parentQuery = entityManager
267 .createNamedQuery(NAMED_QUERY_NAMESPACE + "parentQuery")
268 .setParameter("classID", id.getRootID())
269 .setParameter("categID", id.getID())
270 .setParameter("left", leftRight.leftValue)
271 .setParameter("right", leftRight.rightValue);
272 @SuppressWarnings("unchecked")
273 List<MCRCategoryDTO> resultList = parentQuery.getResultList();
274 MCRCategory category = buildCategoryFromPrefetchedList(resultList, id);
275 List<MCRCategory> parents = new ArrayList<>();
276 while (category.getParent() != null) {
277 category = category.getParent();
278 parents.add(category);
279 }
280 return parents;
281 }
282
283 @Override
284 @SuppressWarnings("unchecked")
285 public List<MCRCategoryID> getRootCategoryIDs() {
286 EntityManager entityManager = MCREntityManagerProvider.getCurrentEntityManager();
287 return entityManager.createNamedQuery(NAMED_QUERY_NAMESPACE + "rootIds").getResultList();
288 }
289
290 @Override
291 @SuppressWarnings("unchecked")
292 public List<MCRCategory> getRootCategories() {
293 EntityManager entityManager = MCREntityManagerProvider.getCurrentEntityManager();
294 List<MCRCategoryDTO> resultList = entityManager.createNamedQuery(NAMED_QUERY_NAMESPACE + "rootCategs")
295 .getResultList();
296 BiConsumer<List<MCRCategory>, MCRCategoryImpl> merge = (l, c) -> {
297 MCRCategoryImpl last = (MCRCategoryImpl) l.get(l.size() - 1);
298 if (last.getInternalID() != c.getInternalID()) {
299 l.add(c);
300 } else {
301 last.getLabels().addAll(c.getLabels());
302 }
303 };
304 return resultList.parallelStream()
305 .map(c -> c.merge(null))
306 .collect(Collector.of(ArrayList::new,
307 (ArrayList<MCRCategory> l, MCRCategoryImpl c) -> {
308 if (l.isEmpty()) {
309 l.add(c);
310 } else {
311 merge.accept(l, c);
312 }
313 }, (l, r) -> {
314 if (l.isEmpty()) {
315 return r;
316 }
317 if (r.isEmpty()) {
318 return l;
319 }
320 MCRCategoryImpl first = (MCRCategoryImpl) r.get(0);
321 merge.accept(l, first);
322 l.addAll(r.subList(1, r.size()));
323 return l;
324 }));
325 }
326
327 @Override
328 public MCRCategory getRootCategory(MCRCategoryID baseID, int childLevel) {
329 return Optional.ofNullable(getCategory(baseID, childLevel))
330 .map(c -> {
331 if (baseID.isRootID()) {
332 return c;
333 }
334 List<MCRCategory> parents = getParents(baseID);
335 MCRCategory parent = parents.get(0);
336 c.getChildren()
337 .stream()
338 .collect(Collectors.toList())
339 .stream()
340 .map(MCRCategoryImpl.class::cast)
341 .peek(MCRCategoryImpl::detachFromParent)
342 .forEachOrdered(parent.getChildren()::add);
343
344 return parents.get(parents.size() - 1);
345 })
346 .orElse(null);
347 }
348
349
350
351
352
353
354 @Override
355 public boolean hasChildren(MCRCategoryID cid) {
356
357
358 return getNumberOfChildren(MCREntityManagerProvider.getCurrentEntityManager(), cid) > 0;
359 }
360
361 @Override
362 public void moveCategory(MCRCategoryID id, MCRCategoryID newParentID) {
363 int index = getNumberOfChildren(MCREntityManagerProvider.getCurrentEntityManager(), newParentID);
364 moveCategory(id, newParentID, index);
365 }
366
367 private MCRCategoryImpl getCommonAncestor(EntityManager entityManager, MCRCategoryImpl node1,
368 MCRCategoryImpl node2) {
369 if (!node1.getRootID().equals(node2.getRootID())) {
370 return null;
371 }
372 if (node1.getLeft() == 0) {
373 return node1;
374 }
375 if (node2.getLeft() == 0) {
376 return node2;
377 }
378 int left = Math.min(node1.getLeft(), node2.getLeft());
379 int right = Math.max(node1.getRight(), node2.getRight());
380 Query q = entityManager.createNamedQuery(NAMED_QUERY_NAMESPACE + "commonAncestor")
381 .setMaxResults(1)
382 .setParameter("left", left)
383 .setParameter("right", right)
384 .setParameter("rootID", node1.getRootID());
385 return getSingleResult(q);
386 }
387
388 @Override
389 public void moveCategory(MCRCategoryID id, MCRCategoryID newParentID, int index) {
390 withoutFlush(MCREntityManagerProvider.getCurrentEntityManager(), true, e -> {
391 MCRCategoryImpl subTree = getByNaturalID(MCREntityManagerProvider.getCurrentEntityManager(), id);
392 MCRCategoryImpl oldParent = (MCRCategoryImpl) subTree.getParent();
393 MCRCategoryImpl newParent = getByNaturalID(MCREntityManagerProvider.getCurrentEntityManager(), newParentID);
394 MCRCategoryImpl commonAncestor = getCommonAncestor(MCREntityManagerProvider.getCurrentEntityManager(),
395 oldParent, newParent);
396 subTree.detachFromParent();
397 LOGGER.debug("Add subtree to new Parent at index: {}", index);
398 newParent.getChildren().add(index, subTree);
399 subTree.parent = newParent;
400 MCREntityManagerProvider.getCurrentEntityManager().flush();
401 int left = commonAncestor.getLeft();
402 commonAncestor.calculateLeftRightAndLevel(Integer.MAX_VALUE / 2, commonAncestor.getLevel());
403 e.flush();
404 commonAncestor.calculateLeftRightAndLevel(left, commonAncestor.getLevel());
405 updateTimeStamp();
406 updateLastModified(id.getRootID());
407 });
408 }
409
410 @Override
411 public MCRCategory removeLabel(MCRCategoryID id, String lang) {
412 EntityManager entityManager = MCREntityManagerProvider.getCurrentEntityManager();
413 MCRCategoryImpl category = getByNaturalID(entityManager, id);
414 category.getLabel(lang).ifPresent(oldLabel -> {
415 category.getLabels().remove(oldLabel);
416 updateTimeStamp();
417 updateLastModified(category.getRootID());
418 });
419 return category;
420 }
421
422 @Override
423 public Collection<MCRCategoryImpl> replaceCategory(MCRCategory newCategory) throws IllegalArgumentException {
424 if (!exist(newCategory.getId())) {
425 throw new IllegalArgumentException(
426 "MCRCategory can not be replaced. MCRCategoryID '" + newCategory.getId() + "' is unknown.");
427 }
428 return withoutFlush(MCREntityManagerProvider.getCurrentEntityManager(), true, em -> {
429 MCRCategoryImpl oldCategory = getByNaturalID(MCREntityManagerProvider.getCurrentEntityManager(),
430 newCategory.getId());
431 int oldLevel = oldCategory.getLevel();
432 int oldLeft = oldCategory.getLeft();
433
434 Map<MCRCategoryID, MCRCategoryImpl> oldMap = toMap(oldCategory);
435 final MCRCategoryImpl copyDeepImpl = copyDeep(newCategory, -1);
436 MCRCategoryImpl newCategoryImpl = MCRCategoryImpl.wrapCategory(copyDeepImpl, oldCategory.getParent(),
437 oldCategory.getRoot());
438
439 Map<MCRCategoryID, MCRCategoryImpl> newMap = toMap(newCategoryImpl);
440
441 oldMap
442 .entrySet()
443 .stream()
444 .filter(c -> !newMap.containsKey(c.getKey()))
445 .map(Map.Entry::getValue)
446 .peek(MCRCategoryDAOImpl::remove)
447 .forEach(c -> LOGGER.info("remove category: {}", c.getId()));
448 oldMap.clear();
449 oldMap.putAll(toMap(oldCategory));
450
451 MCRStreamUtils
452 .flatten(oldCategory, MCRCategory::getChildren, Collection::stream)
453 .filter(c -> newMap.containsKey(c.getId()))
454 .map(MCRCategoryImpl.class::cast)
455 .map(c -> new AbstractMap.SimpleEntry<>(c, newMap.get(c.getId())))
456
457 .peek(e -> syncLabels(e.getValue(), e.getKey()))
458 .forEach(e -> e.getKey().setURI(e.getValue().getURI()));
459
460 oldMap
461 .values()
462 .stream()
463 .filter(c -> c.getInternalID() != oldCategory.getInternalID())
464 .forEach(MCRCategoryImpl::detachFromParent);
465
466 MCRStreamUtils
467 .flatten(newCategoryImpl, MCRCategory::getChildren, Collection::stream)
468 .forEachOrdered(c -> {
469 MCRCategoryImpl oldC = oldMap.get(c.getId());
470 oldC.setChildren(
471 c
472 .getChildren()
473 .stream()
474 .map(cc -> {
475
476 MCRCategoryImpl oldCC = oldMap.get(cc.getId());
477 if (oldCC == null) {
478 oldCC = new MCRCategoryImpl();
479 oldCC.setId(cc.getId());
480 oldCC.setURI(cc.getURI());
481 oldCC.getLabels().addAll(cc.getLabels());
482 oldMap.put(oldCC.getId(), oldCC);
483 }
484 return oldCC;
485 })
486 .collect(Collectors.toList()));
487 });
488 oldCategory.calculateLeftRightAndLevel(Integer.MAX_VALUE / 2, oldLevel);
489 em.flush();
490 oldCategory.calculateLeftRightAndLevel(oldLeft, oldLevel);
491 updateTimeStamp();
492 updateLastModified(newCategory.getId().getRootID());
493 return newMap.values();
494 });
495 }
496
497 private static Map<MCRCategoryID, MCRCategoryImpl> toMap(MCRCategoryImpl oldCategory) {
498 return MCRStreamUtils
499 .flatten(oldCategory, MCRCategory::getChildren, Collection::stream)
500 .collect(Collectors.toMap(MCRCategory::getId, MCRCategoryImpl.class::cast));
501 }
502
503 private static void remove(MCRCategoryImpl category) {
504 if (category.hasChildren()) {
505 int parentPos = category.getPositionInParent();
506 MCRCategoryImpl parent = (MCRCategoryImpl) category.getParent();
507 @SuppressWarnings("unchecked")
508 ArrayList<MCRCategoryImpl> copy = new ArrayList(category.children);
509 copy.forEach(MCRCategoryImpl::detachFromParent);
510 parent.children.addAll(parentPos, copy);
511 copy.forEach(c -> c.parent = parent);
512 }
513 category.detachFromParent();
514 MCREntityManagerProvider.getCurrentEntityManager().remove(category);
515 }
516
517 @Override
518 public MCRCategory setLabel(MCRCategoryID id, MCRLabel label) {
519 EntityManager entityManager = MCREntityManagerProvider.getCurrentEntityManager();
520 MCRCategoryImpl category = getByNaturalID(entityManager, id);
521 category.getLabel(label.getLang()).ifPresent(category.getLabels()::remove);
522 category.getLabels().add(label);
523 updateTimeStamp();
524 updateLastModified(category.getRootID());
525 return category;
526 }
527
528 @Override
529 public MCRCategory setLabels(MCRCategoryID id, SortedSet<MCRLabel> labels) {
530 EntityManager entityManager = MCREntityManagerProvider.getCurrentEntityManager();
531 MCRCategoryImpl category = getByNaturalID(entityManager, id);
532 category.setLabels(labels);
533 updateTimeStamp();
534 updateLastModified(category.getRootID());
535 return category;
536 }
537
538 @Override
539 public MCRCategory setURI(MCRCategoryID id, URI uri) {
540 EntityManager entityManager = MCREntityManagerProvider.getCurrentEntityManager();
541 MCRCategoryImpl category = getByNaturalID(entityManager, id);
542 category.setURI(uri);
543 updateTimeStamp();
544 updateLastModified(category.getRootID());
545 return category;
546 }
547
548 public void repairLeftRightValue(String classID) {
549 final MCRCategoryID rootID = MCRCategoryID.rootID(classID);
550 withoutFlush(MCREntityManagerProvider.getCurrentEntityManager(), true, entityManager -> {
551 MCRCategoryImpl classification = MCRCategoryDAOImpl.getByNaturalID(entityManager, rootID);
552 classification.calculateLeftRightAndLevel(Integer.MAX_VALUE / 2, LEVEL_START_VALUE);
553 entityManager.flush();
554 classification.calculateLeftRightAndLevel(LEFT_START_VALUE, LEVEL_START_VALUE);
555 });
556 }
557
558 @Override
559 public long getLastModified() {
560 return LAST_MODIFIED;
561 }
562
563 private static void updateTimeStamp() {
564 LAST_MODIFIED = System.currentTimeMillis();
565 }
566
567 private static MCRCategoryImpl buildCategoryFromPrefetchedList(List<MCRCategoryDTO> list, MCRCategoryID returnID) {
568 LOGGER.debug(() -> "using prefetched list: " + list);
569 MCRCategoryImpl predecessor = null;
570 for (MCRCategoryDTO entry : list) {
571 predecessor = entry.merge(predecessor);
572 }
573 return MCRStreamUtils.flatten(predecessor.getRoot(), MCRCategory::getChildren, Collection::parallelStream)
574 .filter(c -> c.getId().equals(returnID))
575 .findFirst()
576 .map(MCRCategoryImpl.class::cast)
577 .orElseThrow(() -> new MCRException("Could not find " + returnID + " in database result."));
578 }
579
580 private static MCRCategoryImpl copyDeep(MCRCategory category, int level) {
581 if (category == null) {
582 return null;
583 }
584 MCRCategoryImpl newCateg = new MCRCategoryImpl();
585 int childAmount;
586 try {
587 childAmount = level != 0 ? category.getChildren().size() : 0;
588 } catch (RuntimeException e) {
589 LOGGER.error("Cannot get children size for category: {}", category.getId(), e);
590 throw e;
591 }
592 newCateg.setChildren(new ArrayList<>(childAmount));
593 newCateg.setId(category.getId());
594 newCateg.setLabels(category.getLabels());
595 newCateg.setRoot(category.getRoot());
596 newCateg.setURI(category.getURI());
597 newCateg.setLevel(category.getLevel());
598 if (category instanceof MCRCategoryImpl) {
599
600 newCateg.setLeft(((MCRCategoryImpl) category).getLeft());
601 newCateg.setRight(((MCRCategoryImpl) category).getRight());
602 newCateg.setInternalID(((MCRCategoryImpl) category).getInternalID());
603 }
604 if (childAmount > 0) {
605 for (MCRCategory child : category.getChildren()) {
606 newCateg.getChildren().add(copyDeep(child, level - 1));
607 }
608 }
609 return newCateg;
610 }
611
612
613
614
615
616
617 public static MCRCategoryImpl getByNaturalID(EntityManager entityManager, MCRCategoryID id) {
618 TypedQuery<MCRCategoryImpl> naturalIDQuery = entityManager
619 .createNamedQuery(NAMED_QUERY_NAMESPACE + "byNaturalId", MCRCategoryImpl.class)
620 .setParameter("classID", id.getRootID())
621 .setParameter("categID", id.getID());
622 return getSingleResult(naturalIDQuery);
623 }
624
625 private static void syncLabels(MCRCategoryImpl source, MCRCategoryImpl target) {
626 for (MCRLabel newLabel : source.getLabels()) {
627 Optional<MCRLabel> label = target.getLabel(newLabel.getLang());
628 if (!label.isPresent()) {
629
630 target.getLabels().add(newLabel);
631 }
632 label.ifPresent(oldLabel -> {
633 if (!oldLabel.getText().equals(newLabel.getText())) {
634 oldLabel.setText(newLabel.getText());
635 }
636 if (!oldLabel.getDescription().equals(newLabel.getDescription())) {
637 oldLabel.setDescription(newLabel.getDescription());
638 }
639 });
640 }
641
642 target.getLabels().removeIf(mcrLabel -> !source.getLabel(mcrLabel.getLang()).isPresent());
643 }
644
645 private static MCRCategoryDTO getLeftRightLevelValues(EntityManager entityManager, MCRCategoryID id) {
646 return getSingleResult(entityManager
647 .createNamedQuery(NAMED_QUERY_NAMESPACE + "leftRightLevelQuery")
648 .setParameter("categID", id));
649 }
650
651 private static int getNumberOfChildren(EntityManager entityManager, MCRCategoryID id) {
652 return getSingleResult(entityManager
653 .createNamedQuery(NAMED_QUERY_NAMESPACE + "childCount")
654 .setParameter("classID", id.getRootID())
655 .setParameter("categID", id.getID()));
656 }
657
658 private static void updateLeftRightValue(EntityManager entityManager, String classID, int left,
659 final int increment) {
660 withoutFlush(entityManager, true, e -> {
661 LOGGER.debug("LEFT AND RIGHT values need updates. Left={}, increment by: {}", left, increment);
662 Query leftQuery = e
663 .createNamedQuery(NAMED_QUERY_NAMESPACE + "updateLeft")
664 .setParameter("left", left)
665 .setParameter("increment", increment)
666 .setParameter("classID", classID);
667 int leftChanges = leftQuery.executeUpdate();
668 Query rightQuery = e
669 .createNamedQuery(NAMED_QUERY_NAMESPACE + "updateRight")
670 .setParameter("left", left)
671 .setParameter("increment", increment)
672 .setParameter("classID", classID);
673 int rightChanges = rightQuery.executeUpdate();
674 LOGGER.debug("Updated {} left and {} right values.", leftChanges, rightChanges);
675 });
676 }
677
678
679
680
681
682 protected synchronized void updateLastModified(String root) {
683 LAST_MODIFIED_MAP.put(root, System.currentTimeMillis());
684 }
685
686
687
688
689
690
691 @Override
692 public long getLastModified(String root) {
693 Long long1 = LAST_MODIFIED_MAP.get(root);
694 if (long1 != null) {
695 return long1;
696 }
697 return -1;
698 }
699
700 @SuppressWarnings("unchecked")
701 private static <T> T getSingleResult(Query query) {
702 try {
703 return (T) query.getSingleResult();
704 } catch (NoResultException e) {
705 return null;
706 }
707 }
708
709 private static List<MCRCategory> cast(List<MCRCategoryImpl> list) {
710 @SuppressWarnings({ "unchecked", "rawtypes" })
711 List<MCRCategory> temp = (List) list;
712 return temp;
713 }
714
715 private static <T> T withoutFlush(EntityManager entityManager, boolean flushAtEnd,
716 Function<EntityManager, T> task) {
717 FlushModeType fm = entityManager.getFlushMode();
718 entityManager.setFlushMode(FlushModeType.COMMIT);
719 try {
720 T result = task.apply(entityManager);
721 if (flushAtEnd) {
722 entityManager.flush();
723 }
724 return result;
725 } catch (RuntimeException e) {
726 throw e;
727 } finally {
728 entityManager.setFlushMode(fm);
729 }
730 }
731
732 private static void withoutFlush(EntityManager entityManager, boolean flushAtEnd, Consumer<EntityManager> task) {
733 withoutFlush(entityManager, flushAtEnd, e -> {
734 task.accept(e);
735 return null;
736 });
737 }
738 }