View Javadoc
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 }