Package: DefaultCalendarViewController
DefaultCalendarViewController
name | instruction | branch | complexity | line | method | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
DefaultCalendarViewController(CalendarView, SiteNode, RequestLocaleManager, CalendarDao, TimeProvider) |
|
|
|
|
|
||||||||||||||||||||
createYearLink(int) |
|
|
|
|
|
||||||||||||||||||||
findEntriesForYear(String, int) |
|
|
|
|
|
||||||||||||||||||||
getCurrentYear() |
|
|
|
|
|
||||||||||||||||||||
getRequestedYear(ResourcePath) |
|
|
|
|
|
||||||||||||||||||||
getViewProperties() |
|
|
|
|
|
||||||||||||||||||||
lambda$findEntriesForYear$1(String, int, Integer) |
|
|
|
|
|
||||||||||||||||||||
lambda$findEntriesForYear$2(DefaultCalendarViewController.Entry) |
|
|
|
|
|
||||||||||||||||||||
lambda$prepareRendering$0(String) |
|
|
|
|
|
||||||||||||||||||||
prepareRendering(RenderContext) |
|
|
|
|
|
||||||||||||||||||||
renderView(RenderContext) |
|
|
|
|
|
||||||||||||||||||||
static {...} |
|
|
|
|
|
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.calendar;
28:
29: import javax.annotation.Nonnegative;
30: import javax.annotation.Nonnull;
31: import java.time.ZoneId;
32: import java.time.ZonedDateTime;
33: import java.util.List;
34: import java.util.Map;
35: import java.util.Optional;
36: import java.util.SortedMap;
37: import java.util.TreeMap;
38: import java.util.stream.IntStream;
39: import it.tidalwave.util.TimeProvider;
40: import it.tidalwave.northernwind.core.model.HttpStatusException;
41: import it.tidalwave.northernwind.core.model.RequestLocaleManager;
42: import it.tidalwave.northernwind.core.model.ResourcePath;
43: import it.tidalwave.northernwind.core.model.ResourceProperties;
44: import it.tidalwave.northernwind.core.model.SiteNode;
45: import it.tidalwave.northernwind.frontend.ui.RenderContext;
46: import it.tidalwave.northernwind.frontend.ui.component.calendar.spi.CalendarDao;
47: import lombok.RequiredArgsConstructor;
48: import lombok.ToString;
49: import lombok.extern.slf4j.Slf4j;
50: import static java.util.Collections.emptyMap;
51: import static java.util.stream.Collectors.*;
52: import static javax.servlet.http.HttpServletResponse.*;
53: import static it.tidalwave.northernwind.core.model.Content.P_TITLE;
54:
55: /***********************************************************************************************************************
56: *
57: * <p>A default implementation of the {@link CalendarViewController} that is independent of the presentation technology.
58: * This class is capable to render a yearly calendar with items and related links.</p>
59: *
60: * <p>It accepts a single path parameter {@code year} with selects a given year; otherwise the current year is used.</p>
61: *
62: * <p>Supported properties of the {@link SiteNode}:</p>
63: *
64: * <ul>
65: * <li>{@code P_ENTRIES}: a property with XML format that describes the entries;</li>
66: * <li>{@code P_SELECTED_YEAR}: the year to render (optional, otherwise the current year is used);</li>
67: * <li>{@code P_FIRST_YEAR}: the first available year;</li>
68: * <li>{@code P_LAST_YEAR}: the last available year ;</li>
69: * <li>{@code P_TITLE}: the page title (optional);</li>
70: * <li>{@code P_COLUMNS}: the number of columns of the table to render (optional, defaults to 4).</li>
71: * </ul>
72: *
73: * <p>The property {@code P_ENTRIES} must have the following structure:</p>
74: *
75: * <pre>
76: * <?xml version="1.0" encoding="UTF-8"?>
77: * <calendar>
78: * <year id="2004">
79: * <month id="jan">
80: * <item name="Provence" type="major" link="/diary/2004/01/02/"/>
81: * <item name="Bocca di Magra" link="/diary/2004/01/24/"/>
82: * <item name="Maremma" link="/diary/2004/01/31/"/>
83: * </month>
84: * ...
85: * </year>
86: * ...
87: * </calendar>
88: * </pre>
89: *
90: * <p>Concrete implementations must provide one method for rendering the calendar:</p>
91: *
92: * <ul>
93: * <li>{@link #render(int, int, int, java.util.Map)}</li>
94: * </ul>
95: *
96: * @author Fabrizio Giudici
97: *
98: **********************************************************************************************************************/
99: @RequiredArgsConstructor @Slf4j
100: public abstract class DefaultCalendarViewController implements CalendarViewController
101: {
102: @RequiredArgsConstructor @ToString
103: public static class Entry
104: {
105: public final int month;
106: public final String name;
107: public final String link;
108: public final Optional<String> type;
109: }
110:
111: @Nonnull
112: private final CalendarView view;
113:
114: @Nonnull
115: private final SiteNode siteNode;
116:
117: @Nonnull
118: protected final RequestLocaleManager requestLocaleManager;
119:
120: @Nonnull
121: private final CalendarDao dao;
122:
123: @Nonnull
124: private final TimeProvider timeProvider;
125:
126: private int year;
127:
128: private int firstYear;
129:
130: private int lastYear;
131:
132: private final SortedMap<Integer, List<Entry>> entriesByMonth = new TreeMap<>();
133:
134: /*******************************************************************************************************************
135: *
136: * Compute stuff here, to eventually fail fast.
137: *
138: * {@inheritDoc}
139: *
140: ******************************************************************************************************************/
141: @Override
142: public void prepareRendering (@Nonnull final RenderContext context)
143: throws HttpStatusException
144: {
145: final var requestedYear = getRequestedYear(context.getPathParams(siteNode));
146: final var siteNodeProperties = siteNode.getProperties();
147: final var viewProperties = getViewProperties();
148:
149: year = viewProperties.getProperty(P_SELECTED_YEAR).orElse(requestedYear);
150: firstYear = viewProperties.getProperty(P_FIRST_YEAR).orElse(Math.min(year, requestedYear));
151: lastYear = viewProperties.getProperty(P_LAST_YEAR).orElse(getCurrentYear());
152: log.info("prepareRendering() - {} f: {} l: {} r: {} y: {}", siteNode, firstYear, lastYear, requestedYear, year);
153:
154:• if ((year < firstYear) || (year > lastYear))
155: {
156: throw new HttpStatusException(SC_NOT_FOUND);
157: }
158:
159: entriesByMonth.putAll(siteNodeProperties.getProperty(P_ENTRIES).map(e -> findEntriesForYear(e, year))
160: .orElse(emptyMap()));
161: }
162:
163: /*******************************************************************************************************************
164: *
165: * {@inheritDoc}
166: *
167: ******************************************************************************************************************/
168: @Override
169: public void renderView (@Nonnull final RenderContext context)
170: {
171: render(siteNode.getProperty(P_TITLE), year, firstYear, lastYear, entriesByMonth, getViewProperties().getProperty(P_COLUMNS).orElse(4));
172: }
173:
174: /*******************************************************************************************************************
175: *
176: * Renders the diary.
177: *
178: * @param title a title for the page (optional)
179: * @param year the current year
180: * @param firstYear the first available year
181: * @param lastYear the last available year
182: * @param byMonth a map of entries for the current year indexed by month
183: * @param columns the number of columns of the table to render
184: *
185: ******************************************************************************************************************/
186: protected abstract void render (@Nonnull final Optional<String> title,
187: @Nonnegative final int year,
188: @Nonnegative final int firstYear,
189: @Nonnegative final int lastYear,
190: @Nonnull final SortedMap<Integer, List<Entry>> byMonth,
191: final int columns);
192:
193: /*******************************************************************************************************************
194: *
195: ******************************************************************************************************************/
196: @Nonnull
197: protected final ResourceProperties getViewProperties()
198: {
199: return siteNode.getPropertyGroup(view.getId());
200: }
201:
202: /*******************************************************************************************************************
203: *
204: * Creates a link for the current year.
205: *
206: * @param year the year
207: * @return the link
208: *
209: ******************************************************************************************************************/
210: @Nonnull
211: protected final String createYearLink (final int year)
212: {
213: return siteNode.getSite().createLink(siteNode.getRelativeUri().appendedWith(Integer.toString(year)));
214: }
215:
216: /*******************************************************************************************************************
217: *
218: * Retrieves a map of entries for the given year, indexed by month.
219: *
220: * @param entries the configuration data
221: * @param year the year
222: * @return the map
223: *
224: ******************************************************************************************************************/
225: @Nonnull
226: private Map<Integer, List<Entry>> findEntriesForYear (@Nonnull final String entries, @Nonnegative final int year)
227: {
228: return IntStream.rangeClosed(1, 12).boxed()
229: .flatMap(month -> dao.findMonthlyEntries(siteNode.getSite(), entries, month, year).stream())
230: .collect(groupingBy(e -> e.month));
231: }
232:
233: /*******************************************************************************************************************
234: *
235: * Returns the current year reading it from the path params, or by default from the calendar.
236: *
237: ******************************************************************************************************************/
238: @Nonnegative
239: private int getRequestedYear (@Nonnull final ResourcePath pathParams)
240: throws HttpStatusException
241: {
242:• if (pathParams.getSegmentCount() > 1)
243: {
244: throw new HttpStatusException(SC_BAD_REQUEST);
245: }
246:
247: try
248: {
249:• return pathParams.isEmpty() ? getCurrentYear() : Integer.parseInt(pathParams.getLeading());
250: }
251: catch (NumberFormatException e)
252: {
253: throw new HttpStatusException(SC_BAD_REQUEST);
254: }
255: }
256:
257: /*******************************************************************************************************************
258: *
259: ******************************************************************************************************************/
260: @Nonnegative
261: private int getCurrentYear()
262: {
263: return ZonedDateTime.ofInstant(timeProvider.get(), ZoneId.of("UTC")).getYear();
264: }
265: }