1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package tools.gsf.navigation.siteplan;
17
18 import org.slf4j.Logger;
19 import org.slf4j.LoggerFactory;
20
21 import COM.FutureTense.Interfaces.ICS;
22 import COM.FutureTense.Interfaces.Utilities;
23
24 import com.fatwire.assetapi.data.AssetId;
25 import com.fatwire.cs.core.db.PreparedStmt;
26 import com.fatwire.cs.core.db.StatementParam;
27
28 import tools.gsf.facade.assetapi.AssetIdUtils;
29 import tools.gsf.facade.assetapi.asset.TemplateAssetAccess;
30 import tools.gsf.facade.runtag.render.LogDep;
31 import tools.gsf.facade.sql.IListIterable;
32 import tools.gsf.facade.sql.Row;
33 import tools.gsf.facade.sql.SqlHelper;
34 import tools.gsf.navigation.AssetNode;
35 import tools.gsf.navigation.ConfigurableNode;
36 import tools.gsf.navigation.NavService;
37
38 import java.util.*;
39 import java.util.stream.Collectors;
40 import java.util.stream.Stream;
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100 public abstract class SitePlanNavService<N extends AssetNode<N> & ConfigurableNode<N>> implements NavService<N, AssetId, AssetId> {
101
102 private static final Logger LOG = LoggerFactory.getLogger(SitePlanNavService.class);
103
104 private final ICS ics;
105 private final TemplateAssetAccess dao;
106 private final String sitename;
107 private final Map<AssetId, List<N>> nodesById = new HashMap<>();
108
109
110
111 private final static PreparedStmt FIND_PUBID = new PreparedStmt(
112 "select id from Publication where name = ?",
113 Arrays.asList("Publication"));
114
115 private final static PreparedStmt NAVIGATION_TREE_DUMP = new PreparedStmt(
116 "select spt.* from SITEPLANTREE spt, ASSETPUBLICATION ap " +
117 "where " +
118 "spt.ncode = 'Placed' and " +
119 "spt.otype = ap.assettype and " +
120 "spt.oid = ap.assetid and " +
121 "((ap.pubid = ?) OR (ap.pubid = 0)) " +
122 "order by spt.nparentid, spt.nrank",
123 Arrays.asList("page", "siteplantree", "assetpublication", "Page", "SitePlanTree", "AssetPublication", "PAGE", "SITEPLANTREE", "ASSETPUBLICATION"));
124
125 static {
126 FIND_PUBID.setElement(0, "Publication", "name");
127 NAVIGATION_TREE_DUMP.setElement(0, "ASSETPUBLICATION", "pubid");
128 }
129
130 private final Long _getPubId(String sitename) {
131 final StatementParam params = FIND_PUBID.newParam();
132 params.setString(0, sitename);
133
134 LOG.debug("Executing query to find out pubid for site name {}", sitename);
135
136 IListIterable results = SqlHelper.select(this.ics, FIND_PUBID, params);
137 if (results.size() == 0) {
138 LOG.warn("There is no Publication whose name is '{}'", sitename);
139 return null;
140 } else if (results.size() > 1) {
141 throw new IllegalStateException("There cannot be 2+ sites (publications) in WCS with the same name, yet there seem to be " + results.size() + " sites whose name is '" + sitename + "'");
142 } else {
143 Row row = results.iterator().next();
144 return row.getLong("id");
145 }
146 }
147
148 protected abstract N createAssetNode(AssetId assetId);
149
150 protected TemplateAssetAccess getTemplateAssetAccess() {
151 return this.dao;
152 }
153
154 protected String getSitename() {
155 return this.sitename;
156 }
157
158 protected final ICS getIcs() {
159 return this.ics;
160 }
161
162 public SitePlanNavService(ICS ics, TemplateAssetAccess dao) {
163 this(ics, dao, null);
164 }
165
166 public SitePlanNavService(ICS ics, TemplateAssetAccess dao, String theSitename) {
167 this.ics = ics;
168 this.dao = dao;
169
170 if (!Utilities.goodString(theSitename)) {
171
172 theSitename = ics.GetVar("site");
173 if (!Utilities.goodString(theSitename)) {
174 throw new IllegalStateException("Missing argument sitename. Nav structure cannot be built unless you specify the name of the site (publication) it is for.");
175 }
176 }
177 Long pubId = _getPubId(theSitename);
178 if (pubId == null) {
179 throw new IllegalStateException("Cannot determine pubid for site '" + theSitename + "'. Nav structure cannot be built unless you specify the name of the site (publication) it is for.");
180 }
181
182 this.sitename = theSitename;
183 StatementParam params = NAVIGATION_TREE_DUMP.newParam();
184 params.setLong(0, pubId);
185
186
187 Map<Long, SitePlanTreeData> rowMap = new HashMap<>();
188 Map<Long, List<SitePlanTreeData>> childrenMap = new HashMap<>();
189
190 LOG.debug("Executing SitePlan query for gathering data for nav service...");
191
192 for (Row row : SqlHelper.select(ics, NAVIGATION_TREE_DUMP, params)) {
193 SitePlanTreeData nodeInfo = new SitePlanTreeData(row);
194 LOG.debug("Processing SitePlan row: {}", nodeInfo);
195 rowMap.put(nodeInfo.nid, nodeInfo);
196 LOG.debug("Added row {} to SitePlan rows map under key {}", nodeInfo, nodeInfo.nid);
197 List<SitePlanTreeData> children = childrenMap.get(nodeInfo.nparentid);
198 if (children == null) {
199
200 children = new ArrayList<SitePlanTreeData>();
201 childrenMap.put(nodeInfo.nparentid, children);
202 }
203
204 children.add(nodeInfo);
205 LOG.debug("Added SPT row {} to the list of children of SPT (parent) row {}. That list now looks like this: {}", nodeInfo, nodeInfo.nparentid, children);
206 }
207
208
209 Map<Long, N> nidNodeMap = new HashMap<Long, N>();
210 for (long nid : rowMap.keySet()) {
211 LOG.debug("Will invoke createAssetNode for asset id {}", rowMap.get(nid).assetId);
212 N node = createAssetNode(rowMap.get(nid).assetId);
213 LOG.debug("AssetNode created for asset {}: {}", rowMap.get(nid).assetId, node);
214
215
216 LogDep.logDep(ics, node.getId());
217 LOG.debug("Logged dependency for asset {} inside nav service...", rowMap.get(nid).assetId);
218
219 nidNodeMap.put(nid, node);
220 LOG.debug("Added node {} to nodes map under key {}", node, nid);
221
222
223 AssetId assetId = node.getId();
224 List<N> a1 = nodesById.get(assetId);
225 if (a1 == null) {
226 a1 = Stream.of(node).collect(Collectors.toList());
227 nodesById.put(assetId, a1);
228 } else {
229 a1.add(node);
230 }
231
232 LOG.debug("nodesById map: {}", nodesById);
233
234 }
235
236
237
238 for (long nparentid : childrenMap.keySet()) {
239 LOG.debug("Processing parent-child relationships for SitePlanTree row with nid = {}", nparentid);
240 N parent = nidNodeMap.get(nparentid);
241 if (parent != null) {
242 LOG.debug("AssetNode for nid {} is: {}", nparentid, parent);
243 List<SitePlanTreeData> children = childrenMap.get(nparentid);
244 if (children != null) {
245 LOG.debug("List of children for SPT whose nid = {} is: {}", nparentid, children);
246 for (SitePlanTreeData childRow : children) {
247 N child = nidNodeMap.get(childRow.nid);
248 if (child != null) {
249 parent.addChild(child);
250 child.setParent(parent);
251 LOG.debug("Bound together parent node {} and child node {} as per SPT entry {}", parent, child, childRow);
252 } else {
253 LOG.warn("There could be a problem here... we registered SPT row {} as a child of parent row {} but we did not instantiate a node for that child?", childRow, nparentid);
254 }
255 }
256 } else {
257 LOG.warn("Not sure how we ended up with a a parent node in the children nodes map with no children whatsoever.");
258 }
259 } else {
260 LOG.warn("We have a list of children for SPT entry whose nid = {}. However, there is not a node matching that SPT entry's asset ({}). This is a bit weird, but also legitimate (for instance, the query excludes SiteNavigation assets)", nparentid, rowMap.get(nparentid));
261 }
262 }
263 }
264
265 private static class SitePlanTreeData {
266 final long nid;
267 final long nparentid;
268 final int nrank;
269 final AssetId assetId;
270
271 SitePlanTreeData(Row row) {
272 nid = row.getLong("nid");
273 nparentid = row.getLong("nparentid");
274 nrank = row.getInt("nrank");
275 assetId = AssetIdUtils.createAssetId(row.getString("otype"), row.getLong("oid"));
276 }
277
278 @Override
279 public String toString() {
280 return "SitePlanTreeData{" +
281 "nid=" + nid +
282 ", nparentid=" + nparentid +
283 ", nrank=" + nrank +
284 ", assetId=" + assetId +
285 '}';
286 }
287 }
288
289 public List<N> getNav(AssetId sitePlan) {
290 if (sitePlan == null) {
291 throw new IllegalArgumentException("Null param not allowed");
292 }
293
294
295 List<N> spNodes = nodesById.get(sitePlan);
296 if (spNodes == null) throw new IllegalArgumentException("Could not locate nav structure corresponding to "+sitePlan);
297 if (spNodes.size() > 1) throw new IllegalStateException("Cannot have more than one site plan node with the same id in the tree");
298 N requestedRoot = spNodes.get(0);
299
300
301 return (List<N>) requestedRoot.getChildren();
302 }
303
304 public List<N> getBreadcrumb(AssetId id) {
305
306 if (id == null) {
307 throw new IllegalArgumentException("Cannot calculate breadcrumb of a null asset");
308 }
309
310 Collection<BreadcrumbCandidate<N>> breadcrumbs = new ArrayList<>();
311 LOG.debug("Obtaining all nodes whose ID matches {}", id);
312 List<N> nodes = nodesById.get(id);
313 if (nodes != null) {
314
315 for (N node : nodesById.get(id)) {
316 breadcrumbs.add(new BreadcrumbCandidate<N>(getBreadcrumbForNode(node)));
317 }
318
319
320
321 List<N> breadcrumb = chooseBreadcrumb(breadcrumbs);
322 LOG.debug("This is the preferred breadcrumb for ID {}: {}", id, breadcrumb);
323
324 return breadcrumb;
325 } else {
326 LOG.info("Didn't find any node in this nav structure whose ID matched {}, thus a breadcrumb cannot be calculated. These are all available nodes: {}", id, this.nodesById);
327 LOG.debug("Node not found, getBreadcrumb will return null");
328 return null;
329 }
330
331
332 }
333
334
335
336
337
338
339
340
341
342 protected List<N> getBreadcrumbForNode(N node) {
343 List<N> ancestors = new ArrayList<>();
344 do {
345 ancestors.add(node);
346 node = node.getParent();
347 } while (node != null);
348 Collections.reverse(ancestors);
349 return ancestors;
350 }
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367 protected List<N> chooseBreadcrumb(Collection<BreadcrumbCandidate<N>> candidates) {
368 return candidates.iterator().next().getBreadcrumb();
369 }
370
371 protected static class BreadcrumbCandidate<N extends AssetNode<N>> {
372 private List<N> breadcrumb;
373 private N siteNavigation;
374 private N navStructurePlaceholder;
375
376 public BreadcrumbCandidate(List<N> rawBreadcrumb) {
377 if (rawBreadcrumb.size() < 2) {
378 throw new IllegalArgumentException("Breadcrumb candidate is not valid, insufficient nodes.");
379 }
380 this.siteNavigation = rawBreadcrumb.get(0);
381 if (!this.siteNavigation.getId().getType().equals("SiteNavigation")) {
382 throw new IllegalArgumentException("Breadcrumb candidate is not valid, first node must be the SiteNavigation node, got this instead: " + this.siteNavigation);
383 }
384 this.navStructurePlaceholder = rawBreadcrumb.get(1);
385 if (!this.navStructurePlaceholder.getId().getType().equals("Page")) {
386 throw new IllegalArgumentException("Breadcrumb candidate is not valid, second node must be the Nav Structure Placeholder (Page) node, got this instead: " + this.navStructurePlaceholder);
387 }
388 this.breadcrumb = rawBreadcrumb.subList(2, rawBreadcrumb.size());
389 }
390
391 public N getNavStructurePlaceholder() {
392 return this.navStructurePlaceholder;
393 }
394
395 public List<N> getBreadcrumb() {
396 return this.breadcrumb;
397 }
398 }
399
400 }