1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.openehealth.ipf.commons.ihe.core.chain;
18
19 import org.slf4j.Logger;
20 import org.slf4j.LoggerFactory;
21
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.stream.Collectors;
27
28
29
30
31
32 public class ChainUtils {
33
34 private static final transient Logger LOG = LoggerFactory.getLogger(ChainUtils.class);
35
36
37
38
39
40
41
42
43
44
45 public static <T extends Chainable> List<T> createChain(List<T> initial, Collection<T> custom) {
46 List<T> chain = new ArrayList<>(initial);
47 List<T> unprocessed = new ArrayList<>(custom);
48
49 List<String> chainIds = chain.stream()
50 .map(Chainable::getId)
51 .collect(Collectors.toList());
52
53 while (!unprocessed.isEmpty()) {
54 boolean successful = false;
55
56 Iterator<T> iter = unprocessed.iterator();
57 while (iter.hasNext()) {
58 final T c = iter.next();
59 final String cid = c.getId();
60
61
62 if (chainIds.contains(cid)) {
63 LOG.debug("Element {} is already in the chain, ignore it", cid);
64 iter.remove();
65 successful = true;
66 continue;
67 }
68
69 List<T> unProcessedWithoutC = new ArrayList<>(unprocessed);
70 unProcessedWithoutC.remove(c);
71
72 List<T> unprocessedDependencies = unProcessedWithoutC.stream()
73 .filter(other ->
74 (c.getBefore().contains(other.getId()) && !(other.getAfter().contains(cid))) ||
75 (c.getAfter().contains(other.getId()) && !(other.getBefore().contains(cid)))
76 )
77 .collect(Collectors.toList());
78
79 if (!unprocessedDependencies.isEmpty()) {
80 LOG.debug("Element {} depends on {}", cid,
81 unprocessedDependencies.stream()
82 .map(Chainable::getId)
83 .collect(Collectors.joining(" ")));
84 continue;
85 }
86
87
88
89
90 int position = chain.size();
91
92 List<Integer> beforeIndices = c.getBefore().stream()
93 .map(chainIds::indexOf)
94 .filter(value -> value >= 0)
95 .collect(Collectors.toList());
96
97 List<Integer> afterIndices = c.getAfter().stream()
98 .map(chainIds::indexOf)
99 .filter(value -> value >= 0)
100 .collect(Collectors.toList());
101
102 int minBeforePosition = 0;
103 int maxAfterPosition = 0;
104
105 if (!beforeIndices.isEmpty()) {
106 minBeforePosition = beforeIndices.stream().min(Integer::compare).get();
107 position = minBeforePosition;
108 }
109
110 if (!afterIndices.isEmpty()) {
111 maxAfterPosition = afterIndices.stream().max(Integer::compare).get();
112 position = maxAfterPosition + 1;
113 }
114
115 if (!beforeIndices.isEmpty() && !afterIndices.isEmpty() && (minBeforePosition <= maxAfterPosition)) {
116 throw new ChainException(String.format(
117 "Cannot place %s between %s and %s in %s",
118 cid,
119 chainIds.get(maxAfterPosition),
120 chainIds.get(minBeforePosition),
121 chainIds.stream().collect(Collectors.joining(" "))));
122 }
123
124 chain.add(position, c);
125 chainIds.add(position, cid);
126 iter.remove();
127 LOG.debug("Inserted element {} at position {}", cid, position);
128 successful = true;
129 }
130
131 if (successful) {
132 LOG.debug("Iteration result: {} elements in the chain, {} elements left", chain.size(), unprocessed.size());
133 } else {
134 throw new ChainException("Cannot build a chain, probably there is a dependency loop");
135 }
136 }
137
138 return chain;
139 }
140 }