Skip to content

Package: DefaultSitemapViewController$1

DefaultSitemapViewController$1

nameinstructionbranchcomplexitylinemethod
lambda$visit$0(SiteNode)
M: 0 C: 6
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
lambda$visit$1(SiteNode, SiteNode)
M: 0 C: 5
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
visit(Layout)
M: 18 C: 23
56%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 4 C: 9
69%
M: 0 C: 1
100%
{...}
M: 0 C: 12
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%

Coverage

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