1 package io.github.magwas.inez.query;
2
3 import static io.github.magwas.konveyor.runtime.LogUtil.debug;
4 import static io.github.magwas.konveyor.runtime.LogUtil.warning;
5
6 import java.util.ArrayList;
7 import java.util.List;
8 import java.util.Map;
9 import java.util.Optional;
10 import java.util.Set;
11 import java.util.function.Consumer;
12 import java.util.function.Function;
13 import java.util.stream.Collectors;
14 import java.util.stream.Stream;
15
16 import org.antlr.v4.runtime.misc.ParseCancellationException;
17 import org.springframework.beans.BeansException;
18 import org.springframework.beans.factory.annotation.Autowired;
19 import org.springframework.stereotype.Service;
20
21 import io.github.magwas.inez.Bridi;
22 import io.github.magwas.inez.element.ElementConstants;
23 import io.github.magwas.inez.element.GetRelativeForBridiElementService;
24 import io.github.magwas.inez.parse.ParseTextService;
25 import io.github.magwas.inez.parse.ParserConstants;
26 import io.github.magwas.inez.parse.ParserOutput;
27 import io.github.magwas.inez.storage.FindAllByRepresentationService;
28 import io.github.magwas.inez.storage.FindAllIdByRepresentationService;
29 import io.github.magwas.inez.storage.FindBridiByIdService;
30 import io.github.magwas.inez.storage.GetBridiIdBySelbriAndSumtiIdsService;
31
32 @Service
33 public class QueryProcessorService implements Function<ParserOutput, Stream<Bridi>> {
34
35 @Autowired
36 ParseTextService parseText;
37
38 @Autowired
39 FindAllByRepresentationService findAllByRepresentation;
40
41 @Autowired
42 FindAllIdByRepresentationService findAllIdByRepresentation;
43
44 @Autowired
45 GetBridiIdBySelbriAndSumtiIdsService getBridiIdBySelbriAndSumtiIds;
46
47 @Autowired
48 FindBridiByIdService findBridiById;
49
50 @Autowired
51 GetRelativeForBridiElementService getRelativeForBridiElement;
52
53 @Autowired
54 GetServiceByNameService getServiceByName;
55
56 public Stream<Bridi> apply(final String query) {
57 return parseText.apply(query).map(x -> apply(x)).flatMap(x -> x);
58 }
59
60 @Override
61 public Stream<Bridi> apply(final ParserOutput parserOutput) {
62 String top = parserOutput.top();
63 debug("top:" + top);
64 return query(top, parserOutput.referenceMap());
65 }
66
67 private Stream<Bridi> query(final String top, final Map<String, List<String>> referenceMap) {
68 debug("query(" + top);
69 if (!referenceMap.containsKey(top)) {
70 return resolveSumti(top);
71 }
72 return resolveBridi(top, referenceMap);
73 }
74
75 private Stream<Bridi> resolveSumti(final String top) {
76 debug("resolveSumti(" + top);
77 if (top.startsWith("@")) {
78 debug("byRef");
79 return getBridiByReference(top);
80 } else {
81 debug("byStore");
82 return findAllByRepresentation.apply(top);
83 }
84 }
85
86 private Stream<Bridi> resolveBridi(final String top, final Map<String, List<String>> referenceMap) {
87 debug("resolveBridi(" + top);
88 List<Bridi> byRepresentation = findAllByRepresentation.apply(top).toList();
89 if (!byRepresentation.isEmpty()) {
90 return byRepresentation.stream();
91 }
92 List<String> partList = referenceMap.get(top);
93 List<Set<String>> foundIds = new ArrayList<>();
94 int notAnyIndex = 0;
95 for (int i = 1; i < partList.size(); i++) {
96 final String sumti = partList.get(i);
97 if (!ParserConstants.QUERY_BRIDI_ID.equals(sumti)) notAnyIndex = i;
98 Stream<Bridi> sumtiStream = query(sumti, referenceMap);
99 Stream<String> sumtiIdStream = sumtiStream.map(Bridi::id);
100 Set<String> sumtiIds = sumtiIdStream.collect(Collectors.toSet());
101 foundIds.add(sumtiIds);
102 }
103 debug("partList", partList);
104 BridiFunction fun = functionFor(partList);
105 Stream<Bridi> candidates;
106 if (fun != null) candidates = fun.apply(top, partList, notAnyIndex, foundIds);
107 else {
108 candidates = findCandidates(top, partList, notAnyIndex, foundIds);
109 candidates = filterCandidates(partList, foundIds, candidates);
110 }
111 return candidates;
112 }
113
114 private BridiFunction functionFor(final List<String> partList) {
115 debug("functionFor(", partList);
116 String top = partList.get(0);
117 List<String> ids = findAllIdByRepresentation.apply(top).toList();
118 if (ids.size() != 1) {
119 warning("ambigous representation:", top, ids);
120 return null;
121 }
122 debug("getRelativeForBridiElement.apply(", ids.get(0), ElementConstants.IS_FUNCTION_FOR_ID, 2, 1);
123 List<String> rels = getRelativeForBridiElement
124 .apply(ids.get(0), ElementConstants.IS_FUNCTION_FOR_ID, 2, 1)
125 .toList();
126 debug("rels", rels);
127 if (rels.isEmpty()) return null;
128 if (rels.size() != 1) throw new Error("multiple functions for " + partList);
129 String serviceName = rels.get(0);
130 BridiFunction service;
131 try {
132 service = getServiceByName.apply(serviceName);
133 } catch (BeansException | ClassNotFoundException e) {
134 throw new Error(serviceName + " not found for " + partList, e);
135 }
136 return service;
137 }
138
139 private Stream<Bridi> findCandidates(
140 final String top, final List<String> partList, final int notAnyIndex, final List<Set<String>> foundIds) {
141 debug("findCandidates(" + top, partList, notAnyIndex, foundIds);
142 if (notAnyIndex == 0) throw new ParseCancellationException("only anys in bridi:" + top);
143 String representation = partList.get(0);
144 debug("findAllIdByRepresentation(" + representation);
145 return findAllIdByRepresentation
146 .apply(representation)
147 .peek(x -> debug("-1>", x))
148 .mapMulti((String selbri, Consumer<Stream<Bridi>> consumer) ->
149 findCandidatesForOne(selbri, consumer, top, partList, notAnyIndex, foundIds))
150 .flatMap(c -> c)
151 .peek(x -> debug("-2>", x));
152 }
153
154 private void findCandidatesForOne(
155 final String selbriId,
156 final Consumer<Stream<Bridi>> consumer,
157 final String top,
158 final List<String> partList,
159 final int notAnyIndex,
160 final List<Set<String>> foundIds) {
161 Set<String> sumtiIdSet = foundIds.get(notAnyIndex - 1);
162 debug("findCandidatesForOne(" + top, notAnyIndex, partList);
163 for (String sumtiId : sumtiIdSet) {
164 debug("getBridiBySelbriAndSumtiIds(" + selbriId, sumtiId, notAnyIndex);
165 Stream<Bridi> candidatesForOne = getBridiIdBySelbriAndSumtiIds
166 .apply(selbriId, sumtiId, notAnyIndex)
167 .peek(x -> debug("-id", x))
168 .map(id -> {
169 debug("finding", id);
170 return findBridiById.apply(id).get();
171 });
172 candidatesForOne = candidatesForOne.peek(
173 bridi -> debug("#2>", selbriId, sumtiId, notAnyIndex - 1 + notAnyIndex, bridi));
174 consumer.accept(candidatesForOne);
175 }
176 }
177
178 private Stream<Bridi> filterCandidates(
179 final List<String> partList, final List<Set<String>> foundForSelbries, Stream<Bridi> candidates) {
180 for (int j = 1; j < partList.size(); j++) {
181 final int sumtiIndex = j - 1;
182 final int referenceIndex = j;
183 String sumti = partList.get(referenceIndex);
184 if (!ParserConstants.QUERY_BRIDI_ID.equals(sumti)) {
185 Set<String> allowableSumtiIdSet = foundForSelbries.get(sumtiIndex);
186 debug("filter setup", referenceIndex, allowableSumtiIdSet, partList, sumti);
187 candidates = candidates.filter(bridi -> {
188 List<String> references = bridi.references();
189 if (references.size() <= referenceIndex) return false;
190 String matchedId = references.get(referenceIndex);
191 debug("filtering", referenceIndex, allowableSumtiIdSet, matchedId, partList, bridi);
192 return allowableSumtiIdSet.contains(matchedId);
193 });
194 }
195 }
196 return candidates;
197 }
198
199 private Stream<Bridi> getBridiByReference(final String top) {
200 Stream<Bridi> matchingBridis;
201 Optional<Bridi> bridiP = findBridiById.apply(top.substring(1));
202 if (bridiP.isEmpty()) matchingBridis = Stream.of();
203 else matchingBridis = Stream.of(bridiP.get());
204 return matchingBridis;
205 }
206 }