Package: DefaultSitemapViewController$1
DefaultSitemapViewController$1
name | instruction | branch | complexity | line | method | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
lambda$visit$0(SiteNode) |
|
|
|
|
|
||||||||||||||||||||
lambda$visit$1(SiteNode, SiteNode) |
|
|
|
|
|
||||||||||||||||||||
visit(Layout) |
|
|
|
|
|
||||||||||||||||||||
{...} |
|
|
|
|
|
Coverage
1: /*
2: * *************************************************************************************************************************************************************
3: *
4: * NorthernWind - lightweight CMS
5: * http://tidalwave.it/projects/northernwind
6: *
7: * Copyright (C) 2011 - 2025 Tidalwave s.a.s. (http://tidalwave.it)
8: *
9: * *************************************************************************************************************************************************************
10: *
11: * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
12: * You may obtain a copy of the License at
13: *
14: * http://www.apache.org/licenses/LICENSE-2.0
15: *
16: * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
17: * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
18: *
19: * *************************************************************************************************************************************************************
20: *
21: * git clone https://bitbucket.org/tidalwave/northernwind-src
22: * git clone https://github.com/tidalwave-it/northernwind-src
23: *
24: * *************************************************************************************************************************************************************
25: */
26: package it.tidalwave.northernwind.frontend.ui.component.sitemap;
27:
28: import javax.annotation.CheckForNull;
29: import javax.annotation.Nonnull;
30: import java.time.ZonedDateTime;
31: import java.util.Optional;
32: import java.util.Set;
33: import java.util.SortedSet;
34: import java.util.TreeSet;
35: import it.tidalwave.northernwind.core.model.HttpStatusException;
36: import it.tidalwave.northernwind.core.model.ResourceProperties;
37: import it.tidalwave.northernwind.core.model.Site;
38: import it.tidalwave.northernwind.core.model.SiteNode;
39: import it.tidalwave.northernwind.frontend.ui.Layout;
40: import it.tidalwave.northernwind.frontend.ui.RenderContext;
41: import lombok.EqualsAndHashCode;
42: import lombok.Getter;
43: import lombok.RequiredArgsConstructor;
44: import lombok.ToString;
45: import lombok.extern.slf4j.Slf4j;
46: import static java.util.stream.Collectors.*;
47: import static it.tidalwave.northernwind.core.model.Content.*;
48: import static it.tidalwave.northernwind.core.model.SiteNode._SiteNode_;
49: import static it.tidalwave.northernwind.frontend.ui.component.blog.DefaultBlogViewController.TIME0;
50:
51: /***************************************************************************************************************************************************************
52: *
53: * <p>A default implementation of the {@link SitemapViewController} that is independent of the presentation technology.
54: * This class is capable to render the sitemap of a {@link Site}.</p>
55: *
56: * <p>Supported properties of any {@link SiteNode} in the site:</p>
57: *
58: * <ul>
59: * <li>{@code P_SITEMAP_PRIORITY}: the priority of the {@code SiteNode} - if zero, the node is ignored;</li>
60: * <li>{@code P_SITEMAP_CHILDREN_PRIORITY}: same as {@code P_SITEMAP_PRIORITY}, but for child nodes;</li>
61: * <li>{@code P_LATEST_MODIFICATION_DATE}: the date-time of the latest modification;</li>
62: * <li>{@code P_SITEMAP_CHANGE_FREQUENCY}: the supposed change frequency of the {@code SiteNode}.</li>
63: * </ul>
64: *
65: * <p>Concrete implementations must provide one method for rendering the calendar:</p>
66: *
67: * <ul>
68: * <li>{@link #render(java.util.List)}</li>
69: * </ul>
70: *
71: * @author Fabrizio Giudici
72: *
73: **************************************************************************************************************************************************************/
74: @RequiredArgsConstructor @Slf4j
75: public abstract class DefaultSitemapViewController implements SitemapViewController
76: {
77: @RequiredArgsConstructor @ToString @Getter @EqualsAndHashCode
78: protected static class Entry implements Comparable<Entry>
79: {
80: @Nonnull
81: private final String location;
82:
83: @Nonnull
84: private final ZonedDateTime lastModification;
85:
86: @Nonnull
87: private final String changeFrequency;
88:
89: private final float priority;
90:
91: @Override
92: public int compareTo (@Nonnull final Entry other)
93: {
94: return this.equals(other) ? 0 : this.location.compareTo(other.location);
95: }
96: }
97:
98: @Nonnull
99: private final SiteNode siteNode;
100:
101: @Nonnull
102: private final SitemapView view;
103:
104: /***********************************************************************************************************************************************************
105: * {@inheritDoc}
106: **********************************************************************************************************************************************************/
107: @Override
108: public void renderView (@Nonnull final RenderContext context)
109: {
110: final SortedSet<Entry> entries = new TreeSet<>();
111:
112: siteNode.getSite().find(_SiteNode_).stream().forEach(node ->
113: {
114: final var layout = node.getLayout();
115:
116: // Prevents infinite recursion
117: if (!layout.getTypeUri().startsWith("http://northernwind.tidalwave.it/component/Sitemap/"))
118: {
119: // FIXME: should probably skip children of sitenodes with managePathParams
120: // FIXME: for instance, Calendar would benefit
121: // FIXME: Would Blog benefit? It should, as it manages its own children
122: // FIXME: Places and Themes should move managePathParams=true to each children
123: // FIXME: Problem, the root gallery needs managePathParams=true to load images.xml
124: log.debug(">>>> sitemap processing {} / layout {} ...", node.getRelativeUri(), layout);
125:
126: newEntry(node, null).ifPresent(entries::add);
127:
128: layout.accept(new Visitor<Layout, Void>()
129: {
130: @Override
131: public void visit (@Nonnull final Layout childLayout)
132: {
133: try
134: {
135: entries.addAll(childLayout.createViewAndController(node).getController()
136: .findVirtualSiteNodes()
137: .results()
138: .stream()
139: .peek(e -> log.debug(">>>>>>>> added virtual node: {}", e.getRelativeUri()))
140: .flatMap(childNode -> newEntry(node, childNode).stream())
141: .collect(toList()));
142: }
143: catch (HttpStatusException e)
144: {
145: log.warn("sitemap for {} threw {}", node.getRelativeUri(), e.toString());
146: }
147: catch (Exception e)
148: {
149: log.warn("Skipped item because of {} - root cause {}", e, rootCause(e).toString());
150: }
151: }
152: });
153: }
154: });
155:
156: render(entries);
157: }
158:
159: /***********************************************************************************************************************************************************
160: *
161: **********************************************************************************************************************************************************/
162: protected abstract void render (@Nonnull Set<? extends Entry> entries);
163:
164: /***********************************************************************************************************************************************************
165: *
166: **********************************************************************************************************************************************************/
167: @Nonnull
168: protected final ResourceProperties getViewProperties()
169: {
170: return siteNode.getPropertyGroup(view.getId());
171: }
172:
173: /***********************************************************************************************************************************************************
174: *
175: **********************************************************************************************************************************************************/
176: @Nonnull
177: private static Optional<Entry> newEntry (@Nonnull final SiteNode siteNode,
178: @CheckForNull final SiteNode childSiteNode)
179: {
180: final var node = (childSiteNode != null) ? childSiteNode : siteNode;
181: final var properties = node.getProperties();
182: //
183: // FIXME: if you put the sitemap property straightly into the child site node, you can simplify a lot,
184: // just using a single property and only peeking into a single node
185: final var priorityKey = (childSiteNode == null) ? P_SITEMAP_PRIORITY : P_SITEMAP_CHILDREN_PRIORITY;
186: final float sitemapPriority = siteNode.getProperty(priorityKey).orElse(0.5f);
187:
188: return (sitemapPriority == 0)
189: ? Optional.empty()
190: : Optional.of(new Entry(siteNode.getSite().createLink(node.getRelativeUri()),
191: properties.getProperty(P_LATEST_MODIFICATION_DATE).orElse(TIME0),
192: properties.getProperty(P_SITEMAP_CHANGE_FREQUENCY).orElse("daily"),
193: sitemapPriority));
194: }
195:
196: /***********************************************************************************************************************************************************
197: *
198: **********************************************************************************************************************************************************/
199: @Nonnull
200: private static Throwable rootCause (@Nonnull final Throwable t)
201: {
202: final var cause = t.getCause();
203: return (cause != null) ? rootCause(cause) : t;
204: }
205: }