1 package be
.nikiroo
.fanfix
.library
;
4 import java
.io
.IOException
;
6 import java
.util
.ArrayList
;
8 import java
.util
.TreeSet
;
10 import be
.nikiroo
.fanfix
.Instance
;
11 import be
.nikiroo
.fanfix
.bundles
.UiConfig
;
12 import be
.nikiroo
.fanfix
.bundles
.UiConfigBundle
;
13 import be
.nikiroo
.fanfix
.data
.MetaData
;
14 import be
.nikiroo
.fanfix
.data
.Story
;
15 import be
.nikiroo
.fanfix
.output
.BasicOutput
.OutputType
;
16 import be
.nikiroo
.utils
.Image
;
17 import be
.nikiroo
.utils
.Progress
;
20 * This library will cache another pre-existing {@link BasicLibrary}.
24 public class CacheLibrary
extends BasicLibrary
{
25 private List
<MetaData
> metasReal
;
26 private List
<MetaData
> metasMixed
;
27 private Object metasLock
= new Object();
29 private BasicLibrary lib
;
30 private LocalLibrary cacheLib
;
33 * Create a cache library around the given one.
35 * It will return the same result, but those will be saved to disk at the
36 * same time to be fetched quicker the next time.
39 * the cache directory where to save the files to disk
41 * the original library to wrap
43 * the configuration used to know which kind of default
44 * {@link OutputType} to use for images and non-images stories
46 public CacheLibrary(File cacheDir
, BasicLibrary lib
,
47 UiConfigBundle config
) {
48 this.cacheLib
= new LocalLibrary(cacheDir
, //
49 config
.getString(UiConfig
.GUI_NON_IMAGES_DOCUMENT_TYPE
),
50 config
.getString(UiConfig
.GUI_IMAGES_DOCUMENT_TYPE
), true);
55 public String
getLibraryName() {
56 return lib
.getLibraryName();
60 public Status
getStatus() {
61 return lib
.getStatus();
65 protected List
<MetaData
> getMetas(Progress pg
) throws IOException
{
70 synchronized (metasLock
) {
71 // We make sure that cached metas have precedence
72 if (metasMixed
== null) {
73 if (metasReal
== null) {
74 metasReal
= lib
.getMetas(pg
);
77 metasMixed
= new ArrayList
<MetaData
>();
78 TreeSet
<String
> cachedLuids
= new TreeSet
<String
>();
79 for (MetaData cachedMeta
: cacheLib
.getMetas(null)) {
80 metasMixed
.add(cachedMeta
);
81 cachedLuids
.add(cachedMeta
.getLuid());
83 for (MetaData realMeta
: metasReal
) {
84 if (!cachedLuids
.contains(realMeta
.getLuid())) {
85 metasMixed
.add(realMeta
);
92 return new ArrayList
<MetaData
>(metasMixed
);
96 public synchronized Story
getStory(String luid
, MetaData meta
, Progress pg
)
102 Progress pgImport
= new Progress();
103 Progress pgGet
= new Progress();
106 pg
.addProgress(pgImport
, 3);
107 pg
.addProgress(pgGet
, 1);
109 if (!isCached(luid
)) {
111 cacheLib
.imprt(lib
, luid
, pgImport
);
112 updateMetaCache(metasMixed
, cacheLib
.getInfo(luid
));
114 } catch (IOException e
) {
115 Instance
.getInstance().getTraceHandler().error(e
);
122 String type
= cacheLib
.getOutputType(meta
.isImageDocument());
123 MetaData cachedMeta
= meta
.clone();
124 cachedMeta
.setType(type
);
126 return cacheLib
.getStory(luid
, cachedMeta
, pg
);
130 public synchronized File
getFile(final String luid
, Progress pg
)
136 Progress pgGet
= new Progress();
137 Progress pgRecall
= new Progress();
140 pg
.addProgress(pgGet
, 4);
141 pg
.addProgress(pgRecall
, 1);
143 if (!isCached(luid
)) {
144 getStory(luid
, pgGet
);
148 File file
= cacheLib
.getFile(luid
, pgRecall
);
156 public Image
getCover(final String luid
) throws IOException
{
157 if (isCached(luid
)) {
158 return cacheLib
.getCover(luid
);
161 // We could update the cache here, but it's not easy
162 return lib
.getCover(luid
);
166 public Image
getSourceCover(String source
) throws IOException
{
167 Image custom
= getCustomSourceCover(source
);
168 if (custom
!= null) {
172 Image cached
= cacheLib
.getSourceCover(source
);
173 if (cached
!= null) {
177 return lib
.getSourceCover(source
);
181 public Image
getAuthorCover(String author
) throws IOException
{
182 Image custom
= getCustomAuthorCover(author
);
183 if (custom
!= null) {
187 Image cached
= cacheLib
.getAuthorCover(author
);
188 if (cached
!= null) {
192 return lib
.getAuthorCover(author
);
196 public Image
getCustomSourceCover(String source
) throws IOException
{
197 Image custom
= cacheLib
.getCustomSourceCover(source
);
198 if (custom
== null) {
199 custom
= lib
.getCustomSourceCover(source
);
200 if (custom
!= null) {
201 cacheLib
.setSourceCover(source
, custom
);
209 public Image
getCustomAuthorCover(String author
) throws IOException
{
210 Image custom
= cacheLib
.getCustomAuthorCover(author
);
211 if (custom
== null) {
212 custom
= lib
.getCustomAuthorCover(author
);
213 if (custom
!= null) {
214 cacheLib
.setAuthorCover(author
, custom
);
222 public void setSourceCover(String source
, String luid
) throws IOException
{
223 lib
.setSourceCover(source
, luid
);
224 cacheLib
.setSourceCover(source
, getCover(luid
));
228 public void setAuthorCover(String author
, String luid
) throws IOException
{
229 lib
.setAuthorCover(author
, luid
);
230 cacheLib
.setAuthorCover(author
, getCover(luid
));
234 * Invalidate the {@link Story} cache (when the content has changed, but we
235 * already have it) with the new given meta.
237 * <b>Make sure to always use {@link MetaData} from the cached library in
238 * priority, here.</b>
241 * the {@link Story} to clear from the cache
243 * @throws IOException
244 * in case of IOException
248 protected void updateInfo(MetaData meta
) throws IOException
{
249 throw new IOException(
250 "This method is not supported in a CacheLibrary, please use updateMetaCache");
253 // relplace the meta in Metas by Meta, add it if needed
254 // return TRUE = added
255 private boolean updateMetaCache(List
<MetaData
> metas
, MetaData meta
) {
256 if (meta
!= null && metas
!= null) {
257 synchronized (metasLock
) {
258 boolean changed
= false;
259 for (int i
= 0; i
< metas
.size(); i
++) {
260 if (metas
.get(i
).getLuid().equals(meta
.getLuid())) {
277 protected void invalidateInfo(String luid
) {
279 synchronized (metasLock
) {
284 invalidateInfo(metasReal
, luid
);
285 invalidateInfo(metasMixed
, luid
);
288 cacheLib
.invalidateInfo(luid
);
289 lib
.invalidateInfo(luid
);
292 // luid cannot be null
293 private void invalidateInfo(List
<MetaData
> metas
, String luid
) {
295 synchronized (metasLock
) {
296 for (int i
= 0; i
< metas
.size(); i
++) {
297 if (metas
.get(i
).getLuid().equals(luid
)) {
306 public synchronized Story
save(Story story
, String luid
, Progress pg
)
308 Progress pgLib
= new Progress();
309 Progress pgCacheLib
= new Progress();
316 pg
.addProgress(pgLib
, 1);
317 pg
.addProgress(pgCacheLib
, 1);
319 story
= lib
.save(story
, luid
, pgLib
);
320 updateMetaCache(metasReal
, story
.getMeta());
322 story
= cacheLib
.save(story
, story
.getMeta().getLuid(), pgCacheLib
);
323 updateMetaCache(metasMixed
, story
.getMeta());
329 public void delete(String luid
) throws IOException
{
330 if (isCached(luid
)) {
331 cacheLib
.delete(luid
);
335 invalidateInfo(luid
);
339 protected synchronized void changeSTA(String luid
, String newSource
,
340 String newTitle
, String newAuthor
, Progress pg
) throws IOException
{
345 Progress pgCache
= new Progress();
346 Progress pgOrig
= new Progress();
348 pg
.addProgress(pgCache
, 1);
349 pg
.addProgress(pgOrig
, 1);
351 MetaData meta
= getInfo(luid
);
353 throw new IOException("Story not found: " + luid
);
356 if (isCached(luid
)) {
357 cacheLib
.changeSTA(luid
, newSource
, newTitle
, newAuthor
, pgCache
);
361 lib
.changeSTA(luid
, newSource
, newTitle
, newAuthor
, pgOrig
);
364 meta
.setSource(newSource
);
365 meta
.setTitle(newTitle
);
366 meta
.setAuthor(newAuthor
);
369 if (isCached(luid
)) {
370 updateMetaCache(metasMixed
, meta
);
371 updateMetaCache(metasReal
, lib
.getInfo(luid
));
373 updateMetaCache(metasReal
, meta
);
378 public boolean isCached(String luid
) {
380 return cacheLib
.getInfo(luid
) != null;
381 } catch (IOException e
) {
387 public void clearFromCache(String luid
) throws IOException
{
388 if (isCached(luid
)) {
389 cacheLib
.delete(luid
);
394 public MetaData
imprt(URL url
, Progress pg
) throws IOException
{
399 Progress pgImprt
= new Progress();
400 Progress pgCache
= new Progress();
402 pg
.addProgress(pgImprt
, 7);
403 pg
.addProgress(pgCache
, 3);
405 MetaData meta
= lib
.imprt(url
, pgImprt
);
406 updateMetaCache(metasReal
, meta
);
407 synchronized (metasLock
) {
411 clearFromCache(meta
.getLuid());
417 // All the following methods are only used by Save and Delete in
421 protected int getNextId() {
422 throw new java
.lang
.InternalError("Should not have been called");
426 protected void doDelete(String luid
) throws IOException
{
427 throw new java
.lang
.InternalError("Should not have been called");
431 protected Story
doSave(Story story
, Progress pg
) throws IOException
{
432 throw new java
.lang
.InternalError("Should not have been called");