Package: DiaryPhotoCollectionProvider
DiaryPhotoCollectionProvider
name | instruction | branch | complexity | line | method | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
DiaryPhotoCollectionProvider() |
|
|
|
|
|
||||||||||||||||||||
DiaryPhotoCollectionProvider(String) |
|
|
|
|
|
||||||||||||||||||||
clearCachesImpl() |
|
|
|
|
|
||||||||||||||||||||
entriesFactory(MediaFolder, int) |
|
|
|
|
|
||||||||||||||||||||
findPhotos(MediaFolder) |
|
|
|
|
|
||||||||||||||||||||
lambda$entriesFactory$3(MediaFolder, GalleryDescription) |
|
|
|
|
|
||||||||||||||||||||
lambda$findPhotos$0(Integer, MediaFolder) |
|
|
|
|
|
||||||||||||||||||||
lambda$findPhotos$1(MediaFolder, Integer) |
|
|
|
|
|
||||||||||||||||||||
lambda$findPhotos$2(MediaFolder) |
|
|
|
|
|
||||||||||||||||||||
lambda$parseDiary$4(String, Integer) |
|
|
|
|
|
||||||||||||||||||||
parseDiary(int) |
|
|
|
|
|
||||||||||||||||||||
static {...} |
|
|
|
|
|
Coverage
1: /*
2: * *********************************************************************************************************************
3: *
4: * blueMarine II: Semantic Media Centre
5: * http://tidalwave.it/projects/bluemarine2
6: *
7: * Copyright (C) 2015 - 2021 by 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
12: * the License. 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
17: * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
18: * specific language governing permissions and limitations under the License.
19: *
20: * *********************************************************************************************************************
21: *
22: * git clone https://bitbucket.org/tidalwave/bluemarine2-src
23: * git clone https://github.com/tidalwave-it/bluemarine2-src
24: *
25: * *********************************************************************************************************************
26: */
27: package it.tidalwave.bluemarine2.service.stoppingdown.impl;
28:
29: import javax.annotation.Nonnull;
30: import java.util.ArrayList;
31: import java.util.Collection;
32: import java.util.List;
33: import java.util.Map;
34: import java.util.concurrent.ConcurrentHashMap;
35: import java.util.stream.IntStream;
36: import java.io.IOException;
37: import java.nio.file.Paths;
38: import javax.xml.parsers.ParserConfigurationException;
39: import javax.xml.xpath.XPath;
40: import javax.xml.xpath.XPathExpression;
41: import javax.xml.xpath.XPathExpressionException;
42: import org.w3c.dom.Document;
43: import org.w3c.dom.Node;
44: import org.w3c.dom.NodeList;
45: import org.xml.sax.SAXException;
46: import it.tidalwave.util.annotation.VisibleForTesting;
47: import it.tidalwave.bluemarine2.model.MediaFolder;
48: import it.tidalwave.bluemarine2.model.VirtualMediaFolder;
49: import it.tidalwave.bluemarine2.model.VirtualMediaFolder.EntityCollectionFactory;
50: import it.tidalwave.bluemarine2.model.spi.PathAwareEntity;
51: import it.tidalwave.bluemarine2.model.spi.PathAwareFinder;
52: import lombok.extern.slf4j.Slf4j;
53: import static java.util.Comparator.*;
54: import static java.util.stream.Collectors.*;
55: import static javax.xml.xpath.XPathConstants.NODESET;
56:
57: /***********************************************************************************************************************
58: *
59: * @author Fabrizio Giudici
60: *
61: **********************************************************************************************************************/
62: @Slf4j
63: public class DiaryPhotoCollectionProvider extends PhotoCollectionProviderSupport
64: {
65: private static final String URL_DIARY_TEMPLATE = "%s/diary/%d/";
66:
67: private static final String REGEXP_URL_HOST_AND_PORT = "http:\\/\\/[^\\/]*";
68:
69: private static final XPathExpression XPATH_DIARY_EXPR;
70:
71: /**
72: * A local cache for themes is advisable because multiple calls will be performed.
73: */
74: private final Map<Integer, List<GalleryDescription>> diaryCache = new ConcurrentHashMap<>();
75:
76: /*******************************************************************************************************************
77: *
78: ******************************************************************************************************************/
79: static
80: {
81: try
82: {
83: final XPath xpath = XPATH_FACTORY.newXPath();
84: XPATH_DIARY_EXPR = xpath.compile("//div[@class='nw-calendar']//li/a");
85: }
86: catch (XPathExpressionException e)
87: {
88: throw new ExceptionInInitializerError(e);
89: }
90: }
91:
92: /*******************************************************************************************************************
93: *
94: ******************************************************************************************************************/
95: public DiaryPhotoCollectionProvider()
96: {
97: this(URL_STOPPINGDOWN);
98: }
99:
100: /*******************************************************************************************************************
101: *
102: ******************************************************************************************************************/
103: public DiaryPhotoCollectionProvider (@Nonnull final String baseUrl)
104: {
105: super(baseUrl);
106: }
107:
108: /*******************************************************************************************************************
109: *
110: ******************************************************************************************************************/
111: @Override
112: @Nonnull
113: public PathAwareFinder findPhotos(@Nonnull final MediaFolder parent) {
114: return parent.finderOf(
115: p1 -> IntStream.range(1999, 2016 + 1) // FIXME: use current year
116: .boxed()
117: .map(year -> new VirtualMediaFolder(p1,
118: Paths.get("" + year),
119: "" + year,
120: (EntityCollectionFactory)(p2 -> entriesFactory(p2, year))))
121: .collect(toList()));
122: }
123:
124: /*******************************************************************************************************************
125: *
126: * {@inheritDoc}
127: *
128: ******************************************************************************************************************/
129: @Override
130: protected void clearCachesImpl()
131: {
132: super.clearCachesImpl();
133: diaryCache.clear();
134: }
135:
136: /*******************************************************************************************************************
137: *
138: ******************************************************************************************************************/
139: @Nonnull
140: private Collection<PathAwareEntity> entriesFactory (@Nonnull final MediaFolder parent, final int year)
141: {
142: return parseDiary(year).stream().map(gallery -> gallery.createFolder(parent, this::findPhotos))
143: .collect(toList());
144: }
145:
146: /*******************************************************************************************************************
147: *
148: ******************************************************************************************************************/
149: @Nonnull
150: @VisibleForTesting List<GalleryDescription> parseDiary (final int year)
151: {
152: final String diaryUrl = String.format(URL_DIARY_TEMPLATE, baseUrl, year);
153: log.debug("parseDiary({})", diaryUrl);
154:
155: return diaryCache.computeIfAbsent(year, key ->
156: {
157: try
158: {
159: final Document document = downloadXml(diaryUrl);
160: final NodeList entryNodes = (NodeList)XPATH_DIARY_EXPR.evaluate(document, NODESET);
161: final List<GalleryDescription> galleryDescriptions = new ArrayList<>();
162:
163:• for (int i = 0; i < entryNodes.getLength(); i++)
164: {
165: final Node entryNode = entryNodes.item(i);
166: final String href = getAttribute(entryNode, "href").replaceAll(REGEXP_URL_HOST_AND_PORT, "");
167: final String url = String.format(URL_GALLERY_TEMPLATE, baseUrl, href).replace("//", "/")
168: .replace(":/", "://")
169: .replaceAll("(^.*)\\/([0-9]{2})\\/([0-9]{2})\\/(.*)$", "$1/$2-$3/$4");
170: final String date = href.substring(href.length() - 11, href.length() - 1);
171: final String displayName = date + " - " + entryNode.getTextContent();
172: galleryDescriptions.add(new GalleryDescription(displayName, url));
173: }
174:
175: galleryDescriptions.sort(comparing(GalleryDescription::getUrl));
176:
177: return galleryDescriptions;
178: }
179: catch (SAXException | IOException | XPathExpressionException | ParserConfigurationException e)
180: {
181: throw new RuntimeException(e);
182: }
183: });
184: }
185: }