001/* 002 * Copyright 2011 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.plugins.blog; 017 018import java.time.ZoneId; 019import java.time.ZonedDateTime; 020import java.util.ArrayList; 021import java.util.Calendar; 022import java.util.Collection; 023import java.util.Collections; 024import java.util.Comparator; 025import java.util.Date; 026import java.util.HashMap; 027import java.util.HashSet; 028import java.util.Iterator; 029import java.util.LinkedHashMap; 030import java.util.List; 031import java.util.Map; 032import java.util.Map.Entry; 033import java.util.Set; 034import java.util.concurrent.TimeUnit; 035 036import org.apache.avalon.framework.activity.Initializable; 037import org.apache.avalon.framework.component.Component; 038import org.apache.avalon.framework.logger.AbstractLogEnabled; 039import org.apache.avalon.framework.service.ServiceException; 040import org.apache.avalon.framework.service.ServiceManager; 041import org.apache.avalon.framework.service.Serviceable; 042import org.apache.commons.lang.BooleanUtils; 043 044import org.ametys.cms.ObservationConstants; 045import org.ametys.cms.repository.Content; 046import org.ametys.cms.repository.ContentLanguageExpression; 047import org.ametys.cms.repository.ContentTypeExpression; 048import org.ametys.cms.tag.Tag; 049import org.ametys.cms.tag.TagHelper; 050import org.ametys.cms.tag.TagProviderExtensionPoint; 051import org.ametys.core.observation.Event; 052import org.ametys.core.observation.ObservationManager; 053import org.ametys.core.user.UserIdentity; 054import org.ametys.plugins.blog.posts.PostContentType; 055import org.ametys.plugins.repository.AmetysObjectIterable; 056import org.ametys.plugins.repository.AmetysObjectResolver; 057import org.ametys.plugins.repository.metadata.CompositeMetadata; 058import org.ametys.plugins.repository.provider.WorkspaceSelector; 059import org.ametys.plugins.repository.query.QueryHelper; 060import org.ametys.plugins.repository.query.SortCriteria; 061import org.ametys.plugins.repository.query.expression.AndExpression; 062import org.ametys.plugins.repository.query.expression.Expression; 063import org.ametys.plugins.repository.query.expression.Expression.Operator; 064import org.ametys.plugins.repository.query.expression.MetadataExpression; 065import org.ametys.plugins.repository.query.expression.StringExpression; 066import org.ametys.web.WebConstants; 067import org.ametys.web.repository.content.jcr.DefaultWebContent; 068import org.ametys.web.repository.site.SiteManager; 069import org.ametys.web.site.SiteConfigurationExtensionPoint; 070 071import com.google.common.collect.Multimap; 072import com.google.common.collect.TreeMultimap; 073 074/** 075 * Blog cache manager. 076 */ 077public class BlogCacheManager extends AbstractLogEnabled implements Component, Serviceable, Initializable 078{ 079 /** The name of metadata for date of post */ 080 public static final String POST_METADATA_DATE = "date"; 081 082 /** The avalon role. */ 083 public static final String ROLE = BlogCacheManager.class.getName(); 084 085 /** The ametys object resolver. */ 086 protected AmetysObjectResolver _ametysResolver; 087 088 /** Th tag provider */ 089 protected TagProviderExtensionPoint _tagProviderEP; 090 091 /** The Site manager. */ 092 protected SiteManager _siteManager; 093 094 /** The site configuration extension point */ 095 protected SiteConfigurationExtensionPoint _siteConf; 096 097 /** The blog root page handler. */ 098 protected BlogPageHandler _blogRootHandler; 099 100 /** The workspace selector. */ 101 protected WorkspaceSelector _workspaceSelector; 102 103 /** The Ametys resolver */ 104 protected AmetysObjectResolver _resolver; 105 106 /** The observation manager */ 107 protected ObservationManager _observationManager; 108 109 /** Map of PostDateCache by workspace, site and sitemap/language. */ 110 protected Map<String, Map<String, Map<String, PostDateCache>>> _postsByDate; 111 112 /** Map of PostTagCache by workspace, site and sitemap/language. */ 113 protected Map<String, Map<String, Map<String, PostTagCache>>> _postsByTag; 114 115 /** Map of PostDateCache by workspace, site and sitemap/language. */ 116 protected Map<String, Map<String, Map<String, AllPostCache>>> _allPosts; 117 118 /** Map of FuturePostCache by workspace, site and sitemap/language. */ 119 protected Map<String, Map<String, Map<String, FuturePostCache>>> _futurePostsCache; 120 121 /** Caches are initialized by workspace, site and sitemap/language. */ 122 protected Map<String, Map<String, Map<String, Boolean>>> _initialized; 123 124 /** Future caches are updated by workspace, site and sitemap/language. */ 125 protected Map<String, Map<String, Map<String, Date>>> _lastUpdateFuturePosts; 126 127 @Override 128 public void service(ServiceManager serviceManager) throws ServiceException 129 { 130 _ametysResolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE); 131 _siteManager = (SiteManager) serviceManager.lookup(SiteManager.ROLE); 132 _siteConf = (SiteConfigurationExtensionPoint) serviceManager.lookup(SiteConfigurationExtensionPoint.ROLE); 133 _blogRootHandler = (BlogPageHandler) serviceManager.lookup(BlogPageHandler.ROLE); 134 _workspaceSelector = (WorkspaceSelector) serviceManager.lookup(WorkspaceSelector.ROLE); 135 _tagProviderEP = (TagProviderExtensionPoint) serviceManager.lookup(TagProviderExtensionPoint.ROLE); 136 _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE); 137 _observationManager = (ObservationManager) serviceManager.lookup(ObservationManager.ROLE); 138 } 139 140 @Override 141 public void initialize() throws Exception 142 { 143 _postsByDate = new HashMap<>(); 144 _postsByTag = new HashMap<>(); 145 _allPosts = new HashMap<>(); 146 _futurePostsCache = new HashMap<>(); 147 _initialized = new HashMap<>(); 148 _lastUpdateFuturePosts = new HashMap<>(); 149 } 150 151 /** 152 * Get the years with posts. 153 * @param siteName the site name. 154 * @param language the language. 155 * @param year the year 156 * @return the years with posts as a Set of Integer. 157 */ 158 public boolean hasYear(String siteName, String language, int year) 159 { 160 PostDateCache cache = getPostDateCache(siteName, language); 161 162 return cache.hasYear(year); 163 } 164 165 /** 166 * Get the years with posts. 167 * @param siteName the site name. 168 * @param language the language. 169 * @return the years with posts as a Set of Integer. 170 */ 171 public Set<Integer> getYears(String siteName, String language) 172 { 173 PostDateCache cache = getPostDateCache(siteName, language); 174 175 return cache.getYears(); 176 } 177 178 /** 179 * Get the years with posts. 180 * @param siteName the site name. 181 * @param language the language. 182 * @param year the year 183 * @param month the month 184 * @return the years with posts as a Set of Integer. 185 */ 186 public boolean hasMonth(String siteName, String language, int year, int month) 187 { 188 PostDateCache cache = getPostDateCache(siteName, language); 189 190 return cache.hasMonth(year, month); 191 } 192 193 /** 194 * Get the months with posts for a specified year. 195 * @param siteName the site name. 196 * @param language the language. 197 * @param year the year. 198 * @return the months with posts in the specified year as a Set of Integer. 199 */ 200 public Set<Integer> getMonths(String siteName, String language, int year) 201 { 202 PostDateCache cache = getPostDateCache(siteName, language); 203 204 return cache.getMonths(year); 205 } 206 207 /** 208 * Get the posts for a year-month couple. 209 * @param siteName the site name. 210 * @param language the language. 211 * @param year the year. 212 * @param month the month. 213 * @return the posts in the specified year and month. 214 */ 215 public Map<String, Post> getPostsByMonth(String siteName, String language, int year, int month) 216 { 217 PostDateCache cache = getPostDateCache(siteName, language); 218 219 return cache.getPosts(year, month); 220 } 221 222 /** 223 * Get the posts for a year-month couple. 224 * @param siteName the site name. 225 * @param language the language. 226 * @param year the year. 227 * @param month the month. 228 * @return the posts in the specified year and month. 229 */ 230 public Collection<Post> getSortedPostsByMonth(String siteName, String language, int year, int month) 231 { 232 PostDateCache cache = getPostDateCache(siteName, language); 233 234 List<Post> sortedPosts = new ArrayList<>(cache.getPosts(year, month).values()); 235 236 Collections.sort(sortedPosts, new ReverseDatePostComparator()); 237 238 return sortedPosts; 239 } 240 241 /** 242 * Get the posts for a year. 243 * @param siteName the site name. 244 * @param language the language. 245 * @param year the year. 246 * @return the posts in the specified year. 247 */ 248 public Map<String, Post> getPostsByYear(String siteName, String language, int year) 249 { 250 PostDateCache cache = getPostDateCache(siteName, language); 251 252 return cache.getPosts(year); 253 } 254 255 /** 256 * Get the posts for a year. 257 * @param siteName the site name. 258 * @param language the language. 259 * @param year the year. 260 * @return the posts in the specified year. 261 */ 262 public Collection<Post> getSortedPostsByYear(String siteName, String language, int year) 263 { 264 PostDateCache cache = getPostDateCache(siteName, language); 265 266 List<Post> sortedPosts = new ArrayList<>(cache.getPosts(year).values()); 267 268 Collections.sort(sortedPosts, new ReverseDatePostComparator()); 269 270 return sortedPosts; 271 } 272 273 /** 274 * Get the posts for a year-month couple. 275 * @param siteName the site name. 276 * @param language the language. 277 * @param year the year. 278 * @param month the month. 279 * @param name the name 280 * @return the posts in the specified year and month. 281 */ 282 public Post getPostByName(String siteName, String language, int year, int month, String name) 283 { 284 PostDateCache cache = getPostDateCache(siteName, language); 285 286 return cache.getPostByName(year, month, name); 287 } 288 289 /** 290 * Get the posts for a year-month couple. 291 * @param siteName the site name. 292 * @param language the language. 293 * @param year the year. 294 * @param month the month. 295 * @param id the id of post 296 * @return the posts in the specified year and month. 297 */ 298 public boolean hasPost(String siteName, String language, int year, int month, String id) 299 { 300 PostDateCache cache = getPostDateCache(siteName, language); 301 302 return cache.hasPost(year, month, id); 303 } 304 305 /** 306 * Get the posts for a year-month couple. 307 * @param siteName the site name. 308 * @param language the language. 309 * @param year the year. 310 * @param month the month. 311 * @param name the name of post 312 * @return the posts in the specified year and month. 313 */ 314 public boolean hasPostByName(String siteName, String language, int year, int month, String name) 315 { 316 PostDateCache cache = getPostDateCache(siteName, language); 317 318 return cache.hasPostByName(year, month, name); 319 } 320 321 ///////////////////////////////////////////////// 322 /// POSTS BY TAG METHODS /// 323 ///////////////////////////////////////////////// 324 325 /** 326 * Get the tags for a site and language. 327 * @param siteName The site name 328 * @param language The language 329 * @return the tag list. 330 */ 331 public Set<String> getTags(String siteName, String language) 332 { 333 PostTagCache cache = getPostTagCache(siteName, language); 334 335 return cache.getTags(); 336 } 337 338 /** 339 * Test if a tag exists in the given site and language. 340 * @param siteName The site name 341 * @param language The language 342 * @param tagName The tag name 343 * @return true if the tag exists, false otherwise. 344 */ 345 public boolean hasTag(String siteName, String language, String tagName) 346 { 347 PostTagCache cache = getPostTagCache(siteName, language); 348 349 Map<String, Object> params = new HashMap<>(); 350 params.put("siteName", siteName); 351 Tag tag = _tagProviderEP.getTag(tagName, params); 352 Set<String> descendantNamesAndItSelf = TagHelper.getDescendantNames(tag, true); 353 354 for (String descendantName : descendantNamesAndItSelf) 355 { 356 if (cache.hasTag(descendantName)) 357 { 358 return true; 359 } 360 } 361 362 return false; 363 } 364 365 /** 366 * Get the posts for a given tag. 367 * @param siteName The site name 368 * @param language The language 369 * @param tagName The tag name 370 * @return the tag posts, indexed by ID. 371 */ 372 public Map<String, Post> getPostsByTag(String siteName, String language, String tagName) 373 { 374 PostTagCache cache = getPostTagCache(siteName, language); 375 376 return cache.getPosts(tagName); 377 } 378 379 /** 380 * Get the posts for a given tag. 381 * @param siteName The site name 382 * @param language The language 383 * @param tagName The tag name 384 * @return the tag posts, indexed by ID. 385 */ 386 public Collection<Post> getSortedPostsByTag(String siteName, String language, String tagName) 387 { 388 return getSortedPostsByTag (siteName, language, tagName, false); 389 } 390 391 /** 392 * Get the posts for a given tag. 393 * @param siteName The site name 394 * @param language the language 395 * @param tagName the tag name 396 * @param deepSearch true to perform deep search 397 * @return the tag posts, indexed by ID. 398 */ 399 public Collection<Post> getSortedPostsByTag(String siteName, String language, String tagName, boolean deepSearch) 400 { 401 PostTagCache cache = getPostTagCache(siteName, language); 402 403 List<Post> sortedPosts = new ArrayList<>(cache.getPosts(tagName).values()); 404 405 if (deepSearch) 406 { 407 Map<String, Object> contextualParams = new HashMap<>(); 408 contextualParams.put("siteName", siteName); 409 410 Tag tag = _tagProviderEP.getTag(tagName, contextualParams); 411 if (tag != null) 412 { 413 Set<String> descendantsNames = TagHelper.getDescendantNames(tag, false); 414 for (String descendantsName : descendantsNames) 415 { 416 sortedPosts.addAll(cache.getPosts(descendantsName).values()); 417 } 418 } 419 } 420 421 Collections.sort(sortedPosts, new ReverseDatePostComparator()); 422 423 return sortedPosts; 424 } 425 426 /** 427 * Test if a given post has a tag, by its ID. 428 * @param siteName The site name 429 * @param language The language 430 * @param tagName The tag name 431 * @param postId The id of post 432 * @return true if the given post has the given tag. 433 */ 434 public boolean hasPostByTag(String siteName, String language, String tagName, String postId) 435 { 436 PostTagCache cache = getPostTagCache(siteName, language); 437 438 return cache.hasPost(tagName, postId); 439 } 440 441 /** 442 * Get the posts for a year-month couple. 443 * @param siteName The site name 444 * @param language The language 445 * @param tagName The tag name 446 * @param id The id of post 447 * @return the posts in the specified year and month. 448 */ 449 public Post getPost(String siteName, String language, String tagName, String id) 450 { 451 PostTagCache cache = getPostTagCache(siteName, language); 452 453 return cache.getPost(tagName, id); 454 } 455 456 /** 457 * Get the posts for a year-month couple. 458 * @param siteName The site name 459 * @param language The language 460 * @param tagName The tag name 461 * @param name the name of post 462 * @return the posts in the specified year and month. 463 */ 464 public Post getPostByName(String siteName, String language, String tagName, String name) 465 { 466 PostTagCache cache = getPostTagCache(siteName, language); 467 468 return cache.getPostByName(tagName, name); 469 } 470 471 /** 472 * Get the posts for a year-month couple. 473 * @param siteName The site name 474 * @param language The language 475 * @param tagName The tag name 476 * @param id The id of post 477 * @return the posts in the specified year and month. 478 */ 479 public boolean hasPost(String siteName, String language, String tagName, String id) 480 { 481 PostTagCache cache = getPostTagCache(siteName, language); 482 483 return cache.hasPost(tagName, id); 484 } 485 486 /** 487 * Get the posts for a year-month couple. 488 * @param siteName The site name 489 * @param language The language 490 * @param tagName The tag name 491 * @param name the name of post 492 * @return the posts in the specified year and month. 493 */ 494 public boolean hasPostByName(String siteName, String language, String tagName, String name) 495 { 496 PostTagCache cache = getPostTagCache(siteName, language); 497 498 return cache.hasPostByName(tagName, name); 499 } 500 501 ///////////////////////////////////////////////// 502 /// ALL POSTS METHODS /// 503 ///////////////////////////////////////////////// 504 505 /** 506 * Get a list of all the posts' IDs. 507 * @param siteName the site name. 508 * @param language the language. 509 * @return all the posts' IDs as a Set. 510 */ 511 public Set<String> getPostIds(String siteName, String language) 512 { 513 AllPostCache cache = getAllPostCache(siteName, language); 514 515 return cache.getPostIds(); 516 } 517 518 /** 519 * Get the exhaustive list of Posts. 520 * @param siteName the site name. 521 * @param language the language. 522 * @return the exhaustive list of Posts, indexed by their ID. 523 */ 524 public Map<String, Post> getPosts(String siteName, String language) 525 { 526 AllPostCache cache = getAllPostCache(siteName, language); 527 528 return cache.getPosts(); 529 } 530 531 /** 532 * Get the exhaustive list of Posts. 533 * @param siteName the site name. 534 * @param language the language. 535 * @return the exhaustive list of Posts, indexed by their ID. 536 */ 537 public Collection<Post> getSortedPosts(String siteName, String language) 538 { 539 AllPostCache cache = getAllPostCache(siteName, language); 540 541 List<Post> sortedPosts = new ArrayList<>(cache.getPosts().values()); 542 543 Collections.sort(sortedPosts, new ReverseDatePostComparator()); 544 545 return sortedPosts; 546 } 547 548 /** 549 * Test if a post exists, provided its ID. 550 * @param id the post content ID. 551 * @param siteName the site name. 552 * @param language the language. 553 * @return the Post. 554 */ 555 public Post getPost(String siteName, String language, String id) 556 { 557 AllPostCache cache = getAllPostCache(siteName, language); 558 559 return cache.getPost(id); 560 } 561 562 /** 563 * Test if a post exists, provided its content name. 564 * @param name the post content name. 565 * @param siteName the site name. 566 * @param language the language. 567 * @return true if the post exists. 568 */ 569 public Post getPostByName(String siteName, String language, String name) 570 { 571 AllPostCache cache = getAllPostCache(siteName, language); 572 573 return cache.getPostByName(name); 574 } 575 576 577 /** 578 * Test if a post exists, provided its ID. 579 * @param id the post content ID. 580 * @param siteName the site name. 581 * @param language the language. 582 * @return true if the post exists. 583 */ 584 public boolean hasPost(String siteName, String language, String id) 585 { 586 AllPostCache cache = getAllPostCache(siteName, language); 587 588 return cache.hasPost(id); 589 } 590 591 /** 592 * Test if a post exists, provided its content name. 593 * @param name the post content name. 594 * @param siteName the site name. 595 * @param language the language. 596 * @return true if the post exists. 597 */ 598 public boolean hasPostByName(String siteName, String language, String name) 599 { 600 AllPostCache cache = getAllPostCache(siteName, language); 601 602 return cache.hasPostByName(name); 603 } 604 605 /** 606 * Add a post content to the cache. 607 * @param siteName The site name 608 * @param language The language 609 * @param postContent The content 610 */ 611 public void addPost(String siteName, String language, Content postContent) 612 { 613 PostDateCache dateCache = getPostDateCache(siteName, language); 614 PostTagCache tagCache = getPostTagCache(siteName, language); 615 AllPostCache allCache = getAllPostCache(siteName, language); 616 FuturePostCache futureCache = getFuturePostCache(siteName, language); 617 618 boolean displayFuturePosts = siteName != null ? BooleanUtils.isTrue(_siteConf.getValueAsBoolean(siteName, "display-future-posts")) : true; 619 620 boolean isFuturePost = _isFuturePost(postContent); 621 if (displayFuturePosts || !isFuturePost) 622 { 623 dispatchPost(dateCache, postContent); 624 dispatchPost(tagCache, postContent); 625 dispatchPost(allCache, postContent); 626 } 627 else // => !displayFuturePosts && isFuturePost 628 { 629 dispatchPost(futureCache, postContent); 630 } 631 } 632 633 /** 634 * Modify a post content in the cache. 635 * @param siteName The site name 636 * @param language The language 637 * @param postContent The content 638 */ 639 public void modifyPost(String siteName, String language, Content postContent) 640 { 641 PostDateCache dateCache = getPostDateCache(siteName, language); 642 PostTagCache tagCache = getPostTagCache(siteName, language); 643 AllPostCache allCache = getAllPostCache(siteName, language); 644 FuturePostCache futureCache = getFuturePostCache(siteName, language); 645 646 Post post = allCache.getPost(postContent.getId()); 647 Post futurePost = futureCache.getPost(postContent.getId()); 648 649 boolean displayFuturePosts = siteName != null ? BooleanUtils.isTrue(_siteConf.getValueAsBoolean(siteName, "display-future-posts")) : true; 650 boolean isFuturePost = _isFuturePost(postContent); 651 652 if (displayFuturePosts || !isFuturePost) 653 { 654 if (futurePost != null) 655 { 656 removePost(futureCache, futurePost); 657 } 658 659 if (post == null) 660 { 661 dispatchPost(dateCache, postContent); 662 dispatchPost(tagCache, postContent); 663 dispatchPost(allCache, postContent); 664 } 665 else 666 { 667 modifyPost(dateCache, post, postContent); 668 modifyPost(tagCache, post, postContent); 669 modifyPost(allCache, post, postContent); 670 } 671 } 672 else // => !displayFuturePosts && isFuturePost 673 { 674 if (futurePost == null) 675 { 676 dispatchPost(futureCache, postContent); 677 } 678 else 679 { 680 modifyPost(futureCache, futurePost, postContent); 681 } 682 683 if (post != null) 684 { 685 removePost(dateCache, post); 686 removePost(tagCache, post); 687 removePost(allCache, post); 688 } 689 } 690 } 691 692 /** 693 * Validate a post content. 694 * @param siteName The site name 695 * @param language The language 696 * @param postContent The content 697 */ 698 public void validatePost(String siteName, String language, Content postContent) 699 { 700 boolean displayFuturePosts = siteName != null ? BooleanUtils.isTrue(_siteConf.getValueAsBoolean(siteName, "display-future-posts")) : true; 701 702 // Get the "live" caches. 703 PostDateCache dateCache = getOrCreatePostDateCache(WebConstants.LIVE_WORKSPACE, siteName, language); 704 PostTagCache tagCache = getOrCreatePostTagCache(WebConstants.LIVE_WORKSPACE, siteName, language); 705 AllPostCache allCache = getOrCreateAllPostCache(WebConstants.LIVE_WORKSPACE, siteName, language); 706 FuturePostCache futureCache = getOrCreateFuturePostCache(WebConstants.LIVE_WORKSPACE, siteName, language); 707 708 if (displayFuturePosts || !_isFuturePost(postContent)) 709 { 710 // Dispatch the post in the live caches. 711 removePost(futureCache, postContent); 712 713 dispatchPost(dateCache, postContent); 714 dispatchPost(tagCache, postContent); 715 dispatchPost(allCache, postContent); 716 } 717 else // => !displayFuturePosts && isFuturePost 718 { 719 dispatchPost(futureCache, postContent); 720 721 removePost(dateCache, postContent); 722 removePost(tagCache, postContent); 723 removePost(allCache, postContent); 724 } 725 } 726 727 /** 728 * Remove a post content from the cache. 729 * @param siteName The site name 730 * @param language The language 731 * @param postContent The content 732 */ 733 public void removePost(String siteName, String language, Content postContent) 734 { 735 PostDateCache dateCache = getPostDateCache(siteName, language); 736 PostTagCache tagCache = getPostTagCache(siteName, language); 737 AllPostCache allCache = getAllPostCache(siteName, language); 738 FuturePostCache futureCache = getFuturePostCache(siteName, language); 739 740 removePost(dateCache, postContent); 741 removePost(tagCache, postContent); 742 removePost(allCache, postContent); 743 removePost(futureCache, postContent); 744 } 745 746 /** 747 * Remove a post content from the cache. 748 * @param siteName the site name. 749 * @param postId the post ID. 750 */ 751 public void removePostById(String siteName, String postId) 752 { 753 PostSiteLang postWithSiteLang = getPost(siteName, postId); 754 PostSiteLang futurePostWithSiteLang = getFuturePost(siteName, postId); 755 756 if (postWithSiteLang != null) 757 { 758 Post post = postWithSiteLang.getPost(); 759 String language = postWithSiteLang.getLanguage(); 760 761 // Get cache from current workspace 762 PostDateCache dateCache = getPostDateCache(siteName, language); 763 PostTagCache tagCache = getPostTagCache(siteName, language); 764 AllPostCache allCache = getAllPostCache(siteName, language); 765 766 // Get cache from live workspace 767 PostDateCache workspaceDateCache = getOrCreatePostDateCache(WebConstants.LIVE_WORKSPACE, siteName, language); 768 PostTagCache workspaceTagCache = getOrCreatePostTagCache(WebConstants.LIVE_WORKSPACE, siteName, language); 769 AllPostCache workspaceAllCache = getOrCreateAllPostCache(WebConstants.LIVE_WORKSPACE, siteName, language); 770 771 // Update the cache 772 removePost(dateCache, post); 773 removePost(tagCache, post); 774 removePost(allCache, post); 775 776 removePost(workspaceDateCache, post); 777 removePost(workspaceTagCache, post); 778 removePost(workspaceAllCache, post); 779 } 780 781 if (futurePostWithSiteLang != null) 782 { 783 Post post = futurePostWithSiteLang.getPost(); 784 String language = futurePostWithSiteLang.getLanguage(); 785 786 // Get cache from current workspace 787 FuturePostCache futureCache = getFuturePostCache(siteName, language); 788 789 // Get cache from live workspace 790 FuturePostCache workspaceFutureCache = getOrCreateFuturePostCache(WebConstants.LIVE_WORKSPACE, siteName, language); 791 792 // Update the cache 793 removePost(futureCache, post); 794 removePost(workspaceFutureCache, post); 795 } 796 } 797 798 /** 799 * Clear the caches. 800 */ 801 public void clearCaches() 802 { 803 _postsByDate.clear(); 804 _postsByTag.clear(); 805 _allPosts.clear(); 806 _futurePostsCache.clear(); 807 808 _initialized.clear(); 809 } 810 811 /** 812 * Clear the caches for a given siteName and language. 813 * @param siteName The site name 814 * @param language The language 815 */ 816 public void clearCaches(String siteName, String language) 817 { 818 for (String workspaceName : _postsByDate.keySet()) 819 { 820 PostDateCache dateCache = getOrCreatePostDateCache(workspaceName, siteName, language); 821 dateCache.clear(); 822 } 823 824 for (String workspaceName : _postsByTag.keySet()) 825 { 826 PostTagCache tagCache = getOrCreatePostTagCache(workspaceName, siteName, language); 827 tagCache.clear(); 828 } 829 830 for (String workspaceName : _allPosts.keySet()) 831 { 832 AllPostCache cache = getOrCreateAllPostCache(workspaceName, siteName, language); 833 cache.clear(); 834 } 835 836 for (String workspaceName : _futurePostsCache.keySet()) 837 { 838 FuturePostCache cache = getOrCreateFuturePostCache(workspaceName, siteName, language); 839 cache.clear(); 840 } 841 842 for (String workspaceName : _initialized.keySet()) 843 { 844 setInitialized(workspaceName, siteName, language, Boolean.FALSE); 845 } 846 } 847 848 /** 849 * Get the post date cache. 850 * @param siteName The site name 851 * @param language The language 852 * @return the post date cache for the given site and language. 853 */ 854 protected PostDateCache getPostDateCache(String siteName, String language) 855 { 856 // Get the workspace name. 857 String workspaceName = _workspaceSelector.getWorkspace(); 858 859 // Ensure the cache is created and initialized for the site and language. 860 if (!isInitialized(workspaceName, siteName, language)) 861 { 862 initializeCaches(workspaceName, siteName, language); 863 } 864 865 // Update the future posts cache. 866 _updateFuturePosts(workspaceName, siteName, language); 867 868 return _postsByDate.get(workspaceName).get(siteName).get(language); 869 } 870 871 /** 872 * Get the post date cache. 873 * @param workspaceName The workspace 874 * @param siteName The site name 875 * @param language The language 876 * @return the post date cache for the given site and language. 877 */ 878 protected PostDateCache getOrCreatePostDateCache(String workspaceName, String siteName, String language) 879 { 880 Map<String, Map<String, PostDateCache>> workspaceCache; 881 if (_postsByDate.containsKey(workspaceName)) 882 { 883 workspaceCache = _postsByDate.get(workspaceName); 884 } 885 else 886 { 887 workspaceCache = new HashMap<>(); 888 _postsByDate.put(workspaceName, workspaceCache); 889 } 890 891 Map<String, PostDateCache> siteCache; 892 if (workspaceCache.containsKey(siteName)) 893 { 894 siteCache = workspaceCache.get(siteName); 895 } 896 else 897 { 898 siteCache = new HashMap<>(); 899 workspaceCache.put(siteName, siteCache); 900 } 901 902 PostDateCache cache; 903 if (siteCache.containsKey(language)) 904 { 905 cache = siteCache.get(language); 906 } 907 else 908 { 909 cache = new PostDateCache(); 910 siteCache.put(language, cache); 911 } 912 913 return cache; 914 } 915 916 /** 917 * Get the post tag cache. 918 * @param siteName The site name 919 * @param language The language 920 * @return the post tag cache for the given site and language. 921 */ 922 protected PostTagCache getPostTagCache(String siteName, String language) 923 { 924 // Get the workspace name. 925 String workspaceName = _workspaceSelector.getWorkspace(); 926 927 // Ensure the cache is created and initialized for the site and language. 928 if (!isInitialized(workspaceName, siteName, language)) 929 { 930 initializeCaches(workspaceName, siteName, language); 931 } 932 933 // Update the future posts cache. 934 _updateFuturePosts(workspaceName, siteName, language); 935 936 return _postsByTag.get(workspaceName).get(siteName).get(language); 937 } 938 939 /** 940 * Get the post tag cache. 941 * @param workspaceName The workspace 942 * @param siteName The site name 943 * @param language The language 944 * @return the post tag cache for the given site and language. 945 */ 946 protected PostTagCache getOrCreatePostTagCache(String workspaceName, String siteName, String language) 947 { 948 Map<String, Map<String, PostTagCache>> workspaceCache; 949 if (_postsByTag.containsKey(workspaceName)) 950 { 951 workspaceCache = _postsByTag.get(workspaceName); 952 } 953 else 954 { 955 workspaceCache = new HashMap<>(); 956 _postsByTag.put(workspaceName, workspaceCache); 957 } 958 959 Map<String, PostTagCache> siteCache; 960 if (workspaceCache.containsKey(siteName)) 961 { 962 siteCache = workspaceCache.get(siteName); 963 } 964 else 965 { 966 siteCache = new HashMap<>(); 967 workspaceCache.put(siteName, siteCache); 968 } 969 970 PostTagCache cache; 971 if (siteCache.containsKey(language)) 972 { 973 cache = siteCache.get(language); 974 } 975 else 976 { 977 cache = new PostTagCache(); 978 siteCache.put(language, cache); 979 } 980 981 return cache; 982 } 983 984 /** 985 * Get the exhaustive post cache. 986 * @param siteName The site name 987 * @param language The language 988 * @return the post date cache for the given site and language. 989 */ 990 protected AllPostCache getAllPostCache(String siteName, String language) 991 { 992 // Get the workspace name. 993 String workspaceName = _workspaceSelector.getWorkspace(); 994 995 // Ensure the cache is created and initialized for the site and language. 996 if (!isInitialized(workspaceName, siteName, language)) 997 { 998 initializeCaches(workspaceName, siteName, language); 999 } 1000 1001 // Update the future posts cache. 1002 _updateFuturePosts(workspaceName, siteName, language); 1003 1004 return _allPosts.get(workspaceName).get(siteName).get(language); 1005 } 1006 1007 /** 1008 * Get the exhaustive post cache. 1009 * @param workspaceName The workspace name 1010 * @param siteName The site name 1011 * @param language The language 1012 * @return the post date cache for the given site and language. 1013 */ 1014 protected AllPostCache getOrCreateAllPostCache(String workspaceName, String siteName, String language) 1015 { 1016 Map<String, Map<String, AllPostCache>> workspaceCache; 1017 if (_allPosts.containsKey(workspaceName)) 1018 { 1019 workspaceCache = _allPosts.get(workspaceName); 1020 } 1021 else 1022 { 1023 workspaceCache = new HashMap<>(); 1024 _allPosts.put(workspaceName, workspaceCache); 1025 } 1026 1027 Map<String, AllPostCache> siteCache; 1028 if (workspaceCache.containsKey(siteName)) 1029 { 1030 siteCache = workspaceCache.get(siteName); 1031 } 1032 else 1033 { 1034 siteCache = new HashMap<>(); 1035 workspaceCache.put(siteName, siteCache); 1036 } 1037 1038 AllPostCache cache; 1039 if (siteCache.containsKey(language)) 1040 { 1041 cache = siteCache.get(language); 1042 } 1043 else 1044 { 1045 cache = new AllPostCache(); 1046 siteCache.put(language, cache); 1047 } 1048 1049 return cache; 1050 } 1051 1052 1053 /** 1054 * Get the future post cache. 1055 * @param siteName the name of the site 1056 * @param language the language 1057 * @return the post date cache for the given site and language. 1058 */ 1059 protected FuturePostCache getFuturePostCache(String siteName, String language) 1060 { 1061 // Get the workspace name. 1062 String workspaceName = _workspaceSelector.getWorkspace(); 1063 1064 // Ensure the cache is created and initialized for the site and language. 1065 if (!isInitialized(workspaceName, siteName, language)) 1066 { 1067 initializeCaches(workspaceName, siteName, language); 1068 } 1069 1070 return _futurePostsCache.get(workspaceName).get(siteName).get(language); 1071 } 1072 1073 /** 1074 * Get the future post cache. 1075 * @param workspaceName the name of the workspace 1076 * @param siteName the name of the site 1077 * @param language the language 1078 * @return the future post cache for the given site and language. 1079 */ 1080 protected FuturePostCache getOrCreateFuturePostCache(String workspaceName, String siteName, String language) 1081 { 1082 Map<String, Map<String, FuturePostCache>> workspaceCache; 1083 if (_futurePostsCache.containsKey(workspaceName)) 1084 { 1085 workspaceCache = _futurePostsCache.get(workspaceName); 1086 } 1087 else 1088 { 1089 workspaceCache = new HashMap<>(); 1090 _futurePostsCache.put(workspaceName, workspaceCache); 1091 } 1092 1093 Map<String, FuturePostCache> siteCache; 1094 if (workspaceCache.containsKey(siteName)) 1095 { 1096 siteCache = workspaceCache.get(siteName); 1097 } 1098 else 1099 { 1100 siteCache = new HashMap<>(); 1101 workspaceCache.put(siteName, siteCache); 1102 } 1103 1104 FuturePostCache cache; 1105 if (siteCache.containsKey(language)) 1106 { 1107 cache = siteCache.get(language); 1108 } 1109 else 1110 { 1111 cache = new FuturePostCache(); 1112 siteCache.put(language, cache); 1113 } 1114 1115 return cache; 1116 } 1117 1118 /** 1119 * Get the exhaustive post cache. 1120 * @param workspaceName The workspace name 1121 * @param siteName The site name 1122 * @param language The language 1123 * @return the post date cache for the given site and language. 1124 */ 1125 protected boolean isInitialized(String workspaceName, String siteName, String language) 1126 { 1127 if (_initialized.containsKey(workspaceName)) 1128 { 1129 Map<String, Map<String, Boolean>> workspaceInitialized = _initialized.get(workspaceName); 1130 if (workspaceInitialized.containsKey(siteName)) 1131 { 1132 Map<String, Boolean> siteInitialized = workspaceInitialized.get(siteName); 1133 1134 if (siteInitialized.containsKey(language)) 1135 { 1136 return siteInitialized.get(language); 1137 } 1138 } 1139 } 1140 1141 return false; 1142 } 1143 1144 /** 1145 * Set the initialized map 1146 * @param siteName The site name 1147 * @param language The language 1148 */ 1149 protected void setInitialized(String siteName, String language) 1150 { 1151 setInitialized(siteName, language, Boolean.TRUE); 1152 } 1153 1154 /** 1155 * Set the initialized map 1156 * @param siteName The site name 1157 * @param language The language 1158 * @param initialized true to set initialized 1159 */ 1160 protected void setInitialized(String siteName, String language, Boolean initialized) 1161 { 1162 String workspaceName = _workspaceSelector.getWorkspace(); 1163 1164 setInitialized(workspaceName, siteName, language, initialized); 1165 } 1166 1167 /** 1168 * Set the initialized map 1169 * @param workspaceName The workspace name 1170 * @param siteName The site name 1171 * @param language The language 1172 * @param initialized true if to set initialized 1173 */ 1174 protected void setInitialized(String workspaceName, String siteName, String language, Boolean initialized) 1175 { 1176 Map<String, Map<String, Boolean>> workspaceInitialized; 1177 if (_initialized.containsKey(workspaceName)) 1178 { 1179 workspaceInitialized = _initialized.get(workspaceName); 1180 } 1181 else 1182 { 1183 workspaceInitialized = new HashMap<>(); 1184 _initialized.put(workspaceName, workspaceInitialized); 1185 } 1186 1187 Map<String, Boolean> siteInitialized; 1188 if (workspaceInitialized.containsKey(siteName)) 1189 { 1190 siteInitialized = workspaceInitialized.get(siteName); 1191 } 1192 else 1193 { 1194 siteInitialized = new HashMap<>(); 1195 workspaceInitialized.put(siteName, siteInitialized); 1196 } 1197 1198 siteInitialized.put(language, initialized); 1199 } 1200 1201 /** 1202 * Initialize all the caches. 1203 * @param workspaceName The workspace name 1204 * @param siteName The site name 1205 * @param language The language 1206 */ 1207 protected void initializeCaches(String workspaceName, String siteName, String language) 1208 { 1209 PostDateCache dateCache = getOrCreatePostDateCache(workspaceName, siteName, language); 1210 PostTagCache tagCache = getOrCreatePostTagCache(workspaceName, siteName, language); 1211 AllPostCache allCache = getOrCreateAllPostCache(workspaceName, siteName, language); 1212 FuturePostCache futureCache = getOrCreateFuturePostCache(workspaceName, siteName, language); 1213 1214 ContentTypeExpression cTypeExpr = new ContentTypeExpression(Operator.EQ, PostContentType.CONTENT_TYPE); 1215 Expression siteExpr = new StringExpression(DefaultWebContent.METADATA_SITE, Operator.EQ, siteName); 1216 Expression languageExpr = new ContentLanguageExpression(Operator.EQ, language); 1217 MetadataExpression dateExpr = new MetadataExpression("date"); 1218 1219 Expression expression = new AndExpression(cTypeExpr, siteExpr, languageExpr, dateExpr); 1220 1221 boolean displayFuturePosts = siteName != null ? BooleanUtils.isTrue(_siteConf.getValueAsBoolean(siteName, "display-future-posts")) : true; 1222 1223 SortCriteria sort = new SortCriteria(); 1224 sort.addCriterion("date", false, false); 1225 sort.addJCRPropertyCriterion("ametys:lastModified", false, false); 1226 1227 String query = QueryHelper.getXPathQuery(null, "ametys:content", expression, sort); 1228 1229 try (AmetysObjectIterable<Content> posts = _ametysResolver.query(query);) 1230 { 1231 for (Content postContent : posts) 1232 { 1233 if (displayFuturePosts || !_isFuturePost(postContent)) 1234 { 1235 dispatchPost(dateCache, postContent); 1236 dispatchPost(tagCache, postContent); 1237 dispatchPost(allCache, postContent); 1238 } 1239 else 1240 { 1241 dispatchPost(futureCache, postContent); 1242 } 1243 } 1244 setInitialized(siteName, language); 1245 } 1246 } 1247 1248 private void _updateFuturePosts(String workspaceName, String siteName, String language) 1249 { 1250 Date now = new Date(); 1251 1252 Calendar calendar = Calendar.getInstance(); 1253 calendar.setTime(now); 1254 1255 int minutes = calendar.get(Calendar.MINUTE); 1256 1257 int mod = minutes % 15; 1258 calendar.add(Calendar.MINUTE, -mod); 1259 calendar.set(Calendar.SECOND, 0); 1260 calendar.set(Calendar.MILLISECOND, 0); 1261 1262 Date roundedNow = calendar.getTime(); 1263 1264 if (_needUpdateFuturePosts(workspaceName, siteName, language, roundedNow)) 1265 { 1266 // Update the last update time 1267 _lastUpdateFuturePosts.get(workspaceName).get(siteName).put(language, roundedNow); 1268 1269 // Remove past "future posts" 1270 FuturePostCache futureCache = getOrCreateFuturePostCache(workspaceName, siteName, language); 1271 Collection<Post> pastPosts = futureCache.removePastPosts(); 1272 1273 // Notify that the posts are pasts, and must be visible now. 1274 for (Post pastPost : pastPosts) 1275 { 1276 if (_resolver.hasAmetysObjectForId(pastPost.getId())) 1277 { 1278 Content content = _resolver.resolveById(pastPost.getId()); 1279 1280 Map<String, Object> eventParams = new HashMap<>(); 1281 eventParams.put(ObservationConstants.ARGS_CONTENT, content); 1282 _observationManager.notify(new Event(BlogObservationConstants.EVENT_FUTURE_CONTENT_VISIBLE, new UserIdentity("admin", "admin"), eventParams)); 1283 } 1284 } 1285 } 1286 } 1287 1288 private boolean _needUpdateFuturePosts(String workspaceName, String siteName, String language, Date roundedNow) 1289 { 1290 Date lastUpdate = _getOrCreateLastUpdateFuturePosts(workspaceName, siteName, language); 1291 1292 if (lastUpdate != null) 1293 { 1294 long duration = roundedNow.getTime() - lastUpdate.getTime(); 1295 long diffInMinutes = TimeUnit.MILLISECONDS.toMinutes(duration); 1296 1297 return diffInMinutes >= 15; 1298 } 1299 1300 return true; 1301 } 1302 1303 private Date _getOrCreateLastUpdateFuturePosts(String workspaceName, String siteName, String language) 1304 { 1305 Map<String, Map<String, Date>> workspaceMap; 1306 if (_lastUpdateFuturePosts.containsKey(workspaceName)) 1307 { 1308 workspaceMap = _lastUpdateFuturePosts.get(workspaceName); 1309 } 1310 else 1311 { 1312 workspaceMap = new HashMap<>(); 1313 _lastUpdateFuturePosts.put(workspaceName, workspaceMap); 1314 } 1315 1316 Map<String, Date> siteMap; 1317 if (workspaceMap.containsKey(siteName)) 1318 { 1319 siteMap = workspaceMap.get(siteName); 1320 } 1321 else 1322 { 1323 siteMap = new HashMap<>(); 1324 workspaceMap.put(siteName, siteMap); 1325 } 1326 1327 Date lastUpdate = null; 1328 if (siteMap.containsKey(language)) 1329 { 1330 lastUpdate = siteMap.get(language); 1331 } 1332 1333 return lastUpdate; 1334 } 1335 1336 private boolean _isFuturePost (Content post) 1337 { 1338 Date postDate = post.getMetadataHolder().getDate(POST_METADATA_DATE, null); 1339 return postDate != null && postDate.after(new Date()); 1340 } 1341 1342 /** 1343 * Get a post by its site name and id. 1344 * @param siteName the site name. 1345 * @param postId the post ID. 1346 * @return the Post if it exists in the cache, or null if it doesn't. 1347 */ 1348 protected PostSiteLang getPost(String siteName, String postId) 1349 { 1350 String workspaceName = _workspaceSelector.getWorkspace(); 1351 if (_allPosts.containsKey(workspaceName)) 1352 { 1353 Map<String, Map<String, AllPostCache>> workspacePosts = _allPosts.get(workspaceName); 1354 1355 if (workspacePosts.containsKey(siteName)) 1356 { 1357 Map<String, AllPostCache> sitePosts = workspacePosts.get(siteName); 1358 1359 for (Entry<String, AllPostCache> langPosts : sitePosts.entrySet()) 1360 { 1361 if (langPosts.getValue().hasPost(postId)) 1362 { 1363 String language = langPosts.getKey(); 1364 Post post = langPosts.getValue().getPost(postId); 1365 1366 return new PostSiteLang(post, siteName, language); 1367 } 1368 } 1369 } 1370 } 1371 1372 return null; 1373 } 1374 1375 /** 1376 * Get a future post by its site name and id. 1377 * @param siteName the site name. 1378 * @param postId the post ID. 1379 * @return the Post if it exists in the cache, or null if it doesn't. 1380 */ 1381 protected PostSiteLang getFuturePost(String siteName, String postId) 1382 { 1383 String workspaceName = _workspaceSelector.getWorkspace(); 1384 if (_futurePostsCache.containsKey(workspaceName)) 1385 { 1386 Map<String, Map<String, FuturePostCache>> workspacePosts = _futurePostsCache.get(workspaceName); 1387 1388 if (workspacePosts.containsKey(siteName)) 1389 { 1390 Map<String, FuturePostCache> sitePosts = workspacePosts.get(siteName); 1391 1392 for (Entry<String, FuturePostCache> langPosts : sitePosts.entrySet()) 1393 { 1394 if (langPosts.getValue().hasPost(postId)) 1395 { 1396 String language = langPosts.getKey(); 1397 Post post = langPosts.getValue().getPost(postId); 1398 1399 return new PostSiteLang(post, siteName, language); 1400 } 1401 } 1402 } 1403 } 1404 1405 return null; 1406 } 1407 1408 /** 1409 * Dispatch a post content in the post date cache. 1410 * @param dateCache the post date cache. 1411 * @param content the Post content. 1412 */ 1413 protected void dispatchPost(PostDateCache dateCache, Content content) 1414 { 1415 CompositeMetadata meta = content.getMetadataHolder(); 1416 Date postDate = meta.getDate("date", null); 1417 1418 // Dispatch by date. 1419 if (postDate != null) 1420 { 1421 ZonedDateTime date = postDate.toInstant().atZone(ZoneId.systemDefault()); 1422 int month = date.getMonthValue(); 1423 int year = date.getYear(); 1424 1425 dateCache.addPost(content, year, month); 1426 } 1427 } 1428 1429 /** 1430 * Add a post in the post date cache. 1431 * @param dateCache the post date cache. 1432 * @param post the Post to add. 1433 */ 1434 protected void dispatchPost(PostDateCache dateCache, Post post) 1435 { 1436 Date postDate = post.getDate(); 1437 1438 // Dispatch by date. 1439 if (postDate != null) 1440 { 1441 ZonedDateTime date = postDate.toInstant().atZone(ZoneId.systemDefault()); 1442 int month = date.getMonthValue(); 1443 int year = date.getYear(); 1444 1445 dateCache.addPost(post, year, month); 1446 } 1447 } 1448 1449 /** 1450 * Dispatch a post content in the post tag cache. 1451 * @param tagCache the post tag cache. 1452 * @param content the Post content. 1453 */ 1454 protected void dispatchPost(PostTagCache tagCache, Content content) 1455 { 1456 CompositeMetadata meta = content.getMetadataHolder(); 1457 String[] postTags = meta.getStringArray("tags", new String[0]); 1458 1459 for (String tagName : postTags) 1460 { 1461 tagCache.addPost(content, tagName); 1462 } 1463 } 1464 1465 /** 1466 * Dispatch a post in the post tag cache. 1467 * @param tagCache the post tag cache. 1468 * @param post the Post to add. 1469 */ 1470 protected void dispatchPost(PostTagCache tagCache, Post post) 1471 { 1472 String[] postTags = post.getTags(); 1473 1474 for (String tagName : postTags) 1475 { 1476 tagCache.addPost(post, tagName); 1477 } 1478 } 1479 1480 /** 1481 * Dispatch a post content in the post cache. 1482 * @param cache the post cache. 1483 * @param content the Post content. 1484 */ 1485 protected void dispatchPost(AllPostCache cache, Content content) 1486 { 1487 cache.addPost(content); 1488 } 1489 1490 /** 1491 * Dispatch a post in the post cache. 1492 * @param cache the post cache. 1493 * @param post the Post content. 1494 */ 1495 protected void dispatchPost(AllPostCache cache, Post post) 1496 { 1497 cache.addPost(post); 1498 } 1499 1500 /** 1501 * Dispatch a post content in the future post cache. 1502 * @param cache the future post cache. 1503 * @param content the Post content. 1504 */ 1505 protected void dispatchPost(FuturePostCache cache, Content content) 1506 { 1507 cache.addPost(content); 1508 } 1509 1510 /** 1511 * Remove a post content. 1512 * @param dateCache the post date cache. 1513 * @param content the Post content. 1514 */ 1515 protected void removePost(PostDateCache dateCache, Content content) 1516 { 1517 CompositeMetadata meta = content.getMetadataHolder(); 1518 Date postDate = meta.getDate("date", null); 1519 1520 // Dispatch by date. 1521 if (postDate != null) 1522 { 1523 ZonedDateTime date = postDate.toInstant().atZone(ZoneId.systemDefault()); 1524 int month = date.getMonthValue(); 1525 int year = date.getYear(); 1526 1527 dateCache.removePost(content.getId(), year, month); 1528 } 1529 } 1530 1531 /** 1532 * Remove a post content. 1533 * @param dateCache the post date cache. 1534 * @param post the Post object to remove. 1535 */ 1536 protected void removePost(PostDateCache dateCache, Post post) 1537 { 1538 Date postDate = post.getDate(); 1539 1540 // Dispatch by date. 1541 if (postDate != null) 1542 { 1543 ZonedDateTime date = postDate.toInstant().atZone(ZoneId.systemDefault()); 1544 int month = date.getMonthValue(); 1545 int year = date.getYear(); 1546 1547 dateCache.removePost(post.getId(), year, month); 1548 } 1549 } 1550 1551 /** 1552 * Remove a post content. 1553 * @param cache the post tag cache. 1554 * @param content the Post content. 1555 */ 1556 protected void removePost(PostTagCache cache, Content content) 1557 { 1558 CompositeMetadata meta = content.getMetadataHolder(); 1559 String[] postTags = meta.getStringArray("tags", new String[0]); 1560 1561 for (String tagName : postTags) 1562 { 1563 cache.removePost(content.getId(), tagName); 1564 } 1565 } 1566 1567 /** 1568 * Remove a post content. 1569 * @param cache the post tag cache. 1570 * @param post the Post to remove. 1571 */ 1572 protected void removePost(PostTagCache cache, Post post) 1573 { 1574 String[] postTags = post.getTags(); 1575 1576 for (String tagName : postTags) 1577 { 1578 cache.removePost(post.getId(), tagName); 1579 } 1580 } 1581 1582 /** 1583 * Remove a post content. 1584 * @param cache the post cache. 1585 * @param content the Post content. 1586 */ 1587 protected void removePost(AllPostCache cache, Content content) 1588 { 1589 cache.removePost(content); 1590 } 1591 1592 /** 1593 * Remove a post content. 1594 * @param cache the post cache. 1595 * @param post the Post to remove. 1596 */ 1597 protected void removePost(AllPostCache cache, Post post) 1598 { 1599 cache.removePost(post.getId(), post.getName()); 1600 } 1601 1602 /** 1603 * Remove a future post content. 1604 * @param cache the future post cache. 1605 * @param content the Post content to remove. 1606 */ 1607 protected void removePost(FuturePostCache cache, Content content) 1608 { 1609 cache.removePost(content); 1610 } 1611 1612 /** 1613 * Remove a future post content. 1614 * @param cache the future post cache. 1615 * @param post the Post to remove. 1616 */ 1617 protected void removePost(FuturePostCache cache, Post post) 1618 { 1619 cache.removePost(post); 1620 } 1621 1622 /** 1623 * Remove a post content. 1624 * @param dateCache the post date cache. 1625 * @param post the post content 1626 * @param postContent the post Content. 1627 */ 1628 protected void modifyPost(PostDateCache dateCache, Post post, Content postContent) 1629 { 1630 Date oldDate = post.getDate(); 1631 Date newDate = postContent.getMetadataHolder().getDate("date"); 1632 1633 if (!newDate.equals(oldDate)) 1634 { 1635 if (oldDate != null) 1636 { 1637 ZonedDateTime date = oldDate.toInstant().atZone(ZoneId.systemDefault()); 1638 int month = date.getMonthValue(); 1639 int year = date.getYear(); 1640 dateCache.removePost(postContent.getId(), year, month); 1641 } 1642 1643 ZonedDateTime date = newDate.toInstant().atZone(ZoneId.systemDefault()); 1644 int month = date.getMonthValue(); 1645 int year = date.getYear(); 1646 dateCache.addPost(postContent, year, month); 1647 } 1648 } 1649 1650 /** 1651 * Remove a post content. 1652 * @param cache the post tag cache. 1653 * @param post the post content 1654 * @param postContent the post Content. 1655 */ 1656 protected void modifyPost(PostTagCache cache, Post post, Content postContent) 1657 { 1658 CompositeMetadata meta = postContent.getMetadataHolder(); 1659 String[] oldPostTags = post.getTags(); 1660 String[] newPostTags = meta.getStringArray("tags", new String[0]); 1661 1662 for (String oldTagName : oldPostTags) 1663 { 1664 cache.removePost(postContent.getId(), oldTagName); 1665 } 1666 1667 for (String newTagName : newPostTags) 1668 { 1669 cache.addPost(postContent, newTagName); 1670 } 1671 } 1672 1673 /** 1674 * Modify a post content. 1675 * @param cache the post cache. 1676 * @param post the post content 1677 * @param postContent the post Content. 1678 */ 1679 protected void modifyPost(AllPostCache cache, Post post, Content postContent) 1680 { 1681 cache.removePost(postContent); 1682 cache.addPost(postContent); 1683 } 1684 1685 /** 1686 * Modify a future post content. 1687 * @param cache the post cache. 1688 * @param post the post 1689 * @param postContent the post Content. 1690 */ 1691 protected void modifyPost(FuturePostCache cache, Post post, Content postContent) 1692 { 1693 cache.removePost(postContent); 1694 cache.addPost(postContent); 1695 } 1696 1697 /** 1698 * Class representing a Post in the cache. 1699 */ 1700 public class Post 1701 { 1702 1703 /** The post content id. */ 1704 protected String _id; 1705 1706 /** The post content name. */ 1707 protected String _name; 1708 1709 /** The post date. */ 1710 protected Date _date; 1711 1712 /** The post tags. */ 1713 protected String[] _tags; 1714 1715 /** 1716 * Get the id. 1717 * @return the id 1718 */ 1719 public String getId() 1720 { 1721 return _id; 1722 } 1723 1724 /** 1725 * Set the id. 1726 * @param id the id to set 1727 */ 1728 public void setId(String id) 1729 { 1730 this._id = id; 1731 } 1732 1733 /** 1734 * Get the name. 1735 * @return the name 1736 */ 1737 public String getName() 1738 { 1739 return _name; 1740 } 1741 1742 /** 1743 * Set the name. 1744 * @param name the name to set 1745 */ 1746 public void setName(String name) 1747 { 1748 this._name = name; 1749 } 1750 1751 /** 1752 * Get the tags. 1753 * @return the tags 1754 */ 1755 public String[] getTags() 1756 { 1757 return _tags; 1758 } 1759 1760 /** 1761 * Set the tags. 1762 * @param tags the tags to set 1763 */ 1764 public void setTags(String[] tags) 1765 { 1766 this._tags = new String[tags.length]; 1767 System.arraycopy(tags, 0, _tags, 0, tags.length); 1768 } 1769 1770 /** 1771 * Get the date. 1772 * @return the date 1773 */ 1774 public Date getDate() 1775 { 1776 return _date; 1777 } 1778 1779 /** 1780 * Set the date. 1781 * @param date the date to set 1782 */ 1783 public void setDate(Date date) 1784 { 1785 this._date = date; 1786 } 1787 1788 @Override 1789 public int hashCode() 1790 { 1791 return _id.hashCode(); 1792 } 1793 1794 @Override 1795 public boolean equals(Object obj) 1796 { 1797 if (this == obj) 1798 { 1799 return true; 1800 } 1801 if (obj == null) 1802 { 1803 return false; 1804 } 1805 if (getClass() != obj.getClass()) 1806 { 1807 return false; 1808 } 1809 Post other = (Post) obj; 1810 if (_id == null) 1811 { 1812 if (other._id != null) 1813 { 1814 return false; 1815 } 1816 } 1817 else if (!_id.equals(other._id)) 1818 { 1819 return false; 1820 } 1821 return true; 1822 } 1823 1824 } 1825 1826 /** 1827 * A Post with site name and language. 1828 */ 1829 protected class PostSiteLang extends Post 1830 { 1831 1832 /** The post. */ 1833 protected Post _post; 1834 1835 /** The post site name. */ 1836 protected String _siteName; 1837 1838 /** The post language. */ 1839 protected String _language; 1840 1841 /** 1842 * Default constructor. 1843 */ 1844 public PostSiteLang() 1845 { 1846 // Nothing to do. 1847 } 1848 1849 /** 1850 * Constructor with all parameters. 1851 * @param post the Post. 1852 * @param siteName the site name. 1853 * @param language the language. 1854 */ 1855 public PostSiteLang(Post post, String siteName, String language) 1856 { 1857 super(); 1858 this._post = post; 1859 this._siteName = siteName; 1860 this._language = language; 1861 } 1862 1863 /** 1864 * Get the post. 1865 * @return the post 1866 */ 1867 public Post getPost() 1868 { 1869 return _post; 1870 } 1871 1872 /** 1873 * Set the post. 1874 * @param post the post to set 1875 */ 1876 public void setPost(Post post) 1877 { 1878 this._post = post; 1879 } 1880 1881 /** 1882 * Get the siteName. 1883 * @return the siteName 1884 */ 1885 public String getSiteName() 1886 { 1887 return _siteName; 1888 } 1889 1890 /** 1891 * Set the siteName. 1892 * @param siteName the siteName to set 1893 */ 1894 public void setSiteName(String siteName) 1895 { 1896 this._siteName = siteName; 1897 } 1898 1899 /** 1900 * Get the language. 1901 * @return the language 1902 */ 1903 public String getLanguage() 1904 { 1905 return _language; 1906 } 1907 1908 /** 1909 * Set the language. 1910 * @param language the language to set 1911 */ 1912 public void setLanguage(String language) 1913 { 1914 this._language = language; 1915 } 1916 1917 } 1918 1919 /** 1920 * Post cache by date. 1921 */ 1922 protected class PostDateCache 1923 { 1924 1925 /** The posts, indexed by year, month and ID. */ 1926 private Map<Integer, Map<Integer, Map<String, Post>>> _posts; 1927 1928 /** 1929 * Construct a PostDateCache instance. 1930 */ 1931 public PostDateCache() 1932 { 1933 _posts = new HashMap<>(); 1934 } 1935 1936 /** 1937 * Determines if the year page exists 1938 * @param year the year 1939 * @return true if there are posts for this year 1940 */ 1941 public boolean hasYear(int year) 1942 { 1943 return _posts.containsKey(year); 1944 } 1945 1946 /** 1947 * Get the years with posts. 1948 * @return the years with posts as a Set of Integer. 1949 */ 1950 public Set<Integer> getYears() 1951 { 1952 return _posts.keySet(); 1953 } 1954 1955 /** 1956 * Determines if the page month exists in given year 1957 * @param year The year 1958 * @param month The month 1959 * @return true if there are posts for this year/month couple 1960 */ 1961 public boolean hasMonth(int year, int month) 1962 { 1963 boolean hasMonth = false; 1964 1965 if (_posts.containsKey(year)) 1966 { 1967 hasMonth = _posts.get(year).containsKey(month); 1968 } 1969 1970 return hasMonth; 1971 } 1972 1973 /** 1974 * Get the months with posts for a specified year. 1975 * @param year the year. 1976 * @return the months with posts in the specified year as a Set of Integer. 1977 */ 1978 public Set<Integer> getMonths(int year) 1979 { 1980 Set<Integer> months = new HashSet<>(); 1981 1982 if (_posts.containsKey(year)) 1983 { 1984 months = _posts.get(year).keySet(); 1985 } 1986 1987 return months; 1988 } 1989 1990 /** 1991 * Get the posts for a year couple. 1992 * @param year the year. 1993 * @return the posts in the specified year. 1994 */ 1995 public Map<String, Post> getPosts(int year) 1996 { 1997 Map<String, Post> posts = new HashMap<>(); 1998 1999 if (_posts.containsKey(year)) 2000 { 2001 Map<Integer, Map<String, Post>> months = _posts.get(year); 2002 2003 for (Map<String, Post> month : months.values()) 2004 { 2005 posts.putAll(month); 2006 } 2007 } 2008 2009 return posts; 2010 } 2011 2012 /** 2013 * Get the posts for a year-month couple. 2014 * @param year the year. 2015 * @param month the month. 2016 * @return the posts in the specified year and month. 2017 */ 2018 public Map<String, Post> getPosts(int year, int month) 2019 { 2020 Map<String, Post> posts = new HashMap<>(); 2021 2022 if (_posts.containsKey(year)) 2023 { 2024 Map<Integer, Map<String, Post>> months = _posts.get(year); 2025 2026 if (months.containsKey(month)) 2027 { 2028 posts = months.get(month); 2029 } 2030 } 2031 2032 return posts; 2033 } 2034 2035 /** 2036 * Add a post to the cache. 2037 * @param content The content 2038 * @param year the year 2039 * @param month the month 2040 */ 2041 public void addPost(Content content, int year, int month) 2042 { 2043 Map<String, Post> posts = getOrCreatePostMap(year, month); 2044 2045 CompositeMetadata meta = content.getMetadataHolder(); 2046 2047 Post post = new Post(); 2048 post.setId(content.getId()); 2049 post.setName(content.getName()); 2050 post.setDate(meta.getDate("date")); 2051 post.setTags(meta.getStringArray("tags", new String[0])); 2052 2053 posts.put(post.getId(), post); 2054 } 2055 2056 /** 2057 * Add a post to the cache. 2058 * @param post the post 2059 * @param year the year of the post 2060 * @param month the month of the post 2061 */ 2062 public void addPost(Post post, int year, int month) 2063 { 2064 Map<String, Post> posts = getOrCreatePostMap(year, month); 2065 posts.put(post.getId(), post); 2066 } 2067 2068 /** 2069 * Remove a post from the cache. 2070 * @param contentId The content id 2071 * @param year the year 2072 * @param month the month 2073 */ 2074 public void removePost(String contentId, int year, int month) 2075 { 2076 if (_posts.containsKey(year)) 2077 { 2078 Map<Integer, Map<String, Post>> months = _posts.get(year); 2079 2080 if (months.containsKey(month)) 2081 { 2082 Map<String, Post> posts = months.get(month); 2083 2084 posts.remove(contentId); 2085 if (posts.isEmpty()) 2086 { 2087 months.remove(month); 2088 } 2089 } 2090 2091 if (months.isEmpty()) 2092 { 2093 _posts.remove(year); 2094 } 2095 } 2096 } 2097 2098 /** 2099 * Test if the post with the given ID exists for this year and month. 2100 * @param year the year. 2101 * @param month the month. 2102 * @param id the id of post 2103 * @return the posts in the specified year and month. 2104 */ 2105 public boolean hasPost(int year, int month, String id) 2106 { 2107 return getPosts(year, month).containsKey(id); 2108 } 2109 2110 /** 2111 * Get the posts for a year-month couple. 2112 * @param year the year. 2113 * @param month the month. 2114 * @param name the name of post 2115 * @return the posts in the specified year and month. 2116 */ 2117 public Post getPostByName(int year, int month, String name) 2118 { 2119 Post post = null; 2120 2121 Iterator<Post> posts = getPosts(year, month).values().iterator(); 2122 while (posts.hasNext() && post == null) 2123 { 2124 Post currentPost = posts.next(); 2125 if (currentPost.getName().equals(name)) 2126 { 2127 post = currentPost; 2128 } 2129 } 2130 2131 return post; 2132 } 2133 2134 /** 2135 * Get the posts for a year-month couple. 2136 * @param year the year. 2137 * @param month the month. 2138 * @param name the name of post 2139 * @return the posts in the specified year and month. 2140 */ 2141 public boolean hasPostByName(int year, int month, String name) 2142 { 2143 boolean hasPost = false; 2144 2145 Iterator<Post> posts = getPosts(year, month).values().iterator(); 2146 while (posts.hasNext() && !hasPost) 2147 { 2148 hasPost = posts.next().getName().equals(name); 2149 } 2150 2151 return hasPost; 2152 } 2153 2154 /** 2155 * Clear the cache. 2156 */ 2157 public void clear() 2158 { 2159 _posts.clear(); 2160 } 2161 2162 /** 2163 * Get or create the post set for the specified year and month. 2164 * @param year the year. 2165 * @param month the month. 2166 * @return the post set. 2167 */ 2168 protected Map<String, Post> getOrCreatePostMap(int year, int month) 2169 { 2170 Map<String, Post> posts; 2171 2172 Map<Integer, Map<String, Post>> months; 2173 2174 if (_posts.containsKey(year)) 2175 { 2176 months = _posts.get(year); 2177 } 2178 else 2179 { 2180 months = new HashMap<>(); 2181 _posts.put(year, months); 2182 } 2183 2184 if (months.containsKey(month)) 2185 { 2186 posts = months.get(month); 2187 } 2188 else 2189 { 2190 posts = new LinkedHashMap<>(); 2191 months.put(month, posts); 2192 } 2193 2194 return posts; 2195 } 2196 2197 } 2198 2199 /** 2200 * Post cache by tag. 2201 */ 2202 protected class PostTagCache 2203 { 2204 2205 /** The posts, indexed by tag and ID. */ 2206 private Map<String, Map<String, Post>> _posts; 2207 2208 /** 2209 * Construct a PostTagCache instance. 2210 */ 2211 public PostTagCache() 2212 { 2213 _posts = new HashMap<>(); 2214 } 2215 2216 /** 2217 * Get the years with posts. 2218 * @param tagId The id of tag 2219 * @return the years with posts as a Set of Integer. 2220 */ 2221 public boolean hasTag(String tagId) 2222 { 2223 return _posts.containsKey(tagId); 2224 } 2225 2226 /** 2227 * Get the years with posts. 2228 * @return the years with posts as a Set of Integer. 2229 */ 2230 public Set<String> getTags() 2231 { 2232 return _posts.keySet(); 2233 } 2234 2235 /** 2236 * Get the posts for a year-month couple. 2237 * @param tagId the year. 2238 * @return the posts in the specified year and month. 2239 */ 2240 public Map<String, Post> getPosts(String tagId) 2241 { 2242 Map<String, Post> posts = new HashMap<>(); 2243 2244 if (_posts.containsKey(tagId)) 2245 { 2246 posts = _posts.get(tagId); 2247 } 2248 2249 return posts; 2250 } 2251 2252 /** 2253 * Add a post to the cache. 2254 * @param content The content 2255 * @param tagId the tag ID. 2256 */ 2257 public void addPost(Content content, String tagId) 2258 { 2259 Map<String, Post> posts = getOrCreatePostMap(tagId); 2260 2261 CompositeMetadata meta = content.getMetadataHolder(); 2262 2263 Post post = new Post(); 2264 post.setId(content.getId()); 2265 post.setName(content.getName()); 2266 post.setDate(meta.getDate("date")); 2267 post.setTags(meta.getStringArray("tags", new String[0])); 2268 2269 posts.put(post.getId(), post); 2270 } 2271 2272 /** 2273 * Add a post to the cache. 2274 * @param post the post 2275 * @param tagId the tag ID. 2276 */ 2277 public void addPost(Post post, String tagId) 2278 { 2279 Map<String, Post> posts = getOrCreatePostMap(tagId); 2280 posts.put(post.getId(), post); 2281 } 2282 2283 /** 2284 * Remove a post from the cache. 2285 * @param contentId The id of content 2286 * @param tagId the id of tag 2287 */ 2288 public void removePost(String contentId, String tagId) 2289 { 2290 if (_posts.containsKey(tagId)) 2291 { 2292 Map<String, Post> posts = _posts.get(tagId); 2293 2294 posts.remove(contentId); 2295 if (posts.isEmpty()) 2296 { 2297 _posts.remove(tagId); 2298 } 2299 } 2300 } 2301 2302 /** 2303 * Test if the post with the given ID exists for this year and month. 2304 * @param tagId the tag id. 2305 * @param id the post ID. 2306 * @return the posts in the specified year and month. 2307 */ 2308 public Post getPost(String tagId, String id) 2309 { 2310 return getPosts(tagId).get(id); 2311 } 2312 2313 /** 2314 * Test if the post with the given ID exists for this year and month. 2315 * @param tagId the tag id. 2316 * @param id the post ID. 2317 * @return the posts in the specified year and month. 2318 */ 2319 public boolean hasPost(String tagId, String id) 2320 { 2321 return getPosts(tagId).containsKey(id); 2322 } 2323 2324 /** 2325 * Get the posts for a year-month couple. 2326 * @param tagId the year. 2327 * @param name The name of post 2328 * @return the posts in the specified year and month. 2329 */ 2330 public Post getPostByName(String tagId, String name) 2331 { 2332 Post post = null; 2333 2334 Iterator<Post> posts = getPosts(tagId).values().iterator(); 2335 while (posts.hasNext() && post == null) 2336 { 2337 Post currentPost = posts.next(); 2338 if (currentPost.getName().equals(name)) 2339 { 2340 post = currentPost; 2341 } 2342 } 2343 2344 return post; 2345 } 2346 2347 /** 2348 * Get the posts for a year-month couple. 2349 * @param tagId the year. 2350 * @param name the name of post 2351 * @return the posts in the specified year and month. 2352 */ 2353 public boolean hasPostByName(String tagId, String name) 2354 { 2355 boolean hasPost = false; 2356 2357 Iterator<Post> posts = getPosts(tagId).values().iterator(); 2358 while (posts.hasNext() && !hasPost) 2359 { 2360 hasPost = posts.next().getName().equals(name); 2361 } 2362 2363 return hasPost; 2364 } 2365 2366 /** 2367 * Clear the cache. 2368 */ 2369 public void clear() 2370 { 2371 _posts.clear(); 2372 } 2373 2374 /** 2375 * Get or create the post set for the specified year and month. 2376 * @param tagId the year. 2377 * @return the post set, indexed by id. 2378 */ 2379 protected Map<String, Post> getOrCreatePostMap(String tagId) 2380 { 2381 Map<String, Post> posts; 2382 2383 if (_posts.containsKey(tagId)) 2384 { 2385 posts = _posts.get(tagId); 2386 } 2387 else 2388 { 2389 posts = new LinkedHashMap<>(); 2390 _posts.put(tagId, posts); 2391 } 2392 2393 return posts; 2394 } 2395 2396 } 2397 2398 /** 2399 * Cache of all the posts. 2400 */ 2401 protected class AllPostCache 2402 { 2403 2404 /** The posts, indexed by ID. */ 2405 private Map<String, Post> _posts; 2406 2407 /** The posts, indexed by name. */ 2408 private Map<String, Post> _postsByName; 2409 2410 /** 2411 * Construct a AllPostCache instance. 2412 */ 2413 public AllPostCache() 2414 { 2415 _posts = new LinkedHashMap<>(); 2416 _postsByName = new LinkedHashMap<>(); 2417 } 2418 2419 /** 2420 * Get a list of all the posts' IDs. 2421 * @return all the posts' IDs as a Set. 2422 */ 2423 public Set<String> getPostIds() 2424 { 2425 return Collections.unmodifiableSet(_posts.keySet()); 2426 } 2427 2428 /** 2429 * Get the exhaustive list of Posts. 2430 * @return the exhaustive list of Posts. 2431 */ 2432 public Map<String, Post> getPosts() 2433 { 2434 return Collections.unmodifiableMap(_posts); 2435 } 2436 2437 /** 2438 * Test if a post exists, provided its ID. 2439 * @param id the post content ID. 2440 * @return true if the post exists. 2441 */ 2442 public Post getPost(String id) 2443 { 2444 return _posts.get(id); 2445 } 2446 2447 /** 2448 * Test if a post exists, provided its ID. 2449 * @param id the post content ID. 2450 * @return true if the post exists. 2451 */ 2452 public boolean hasPost(String id) 2453 { 2454 return _posts.containsKey(id); 2455 } 2456 2457 /** 2458 * Test if a post exists, provided its content name. 2459 * @param name the post content name. 2460 * @return true if the post exists. 2461 */ 2462 public Post getPostByName(String name) 2463 { 2464 return _postsByName.get(name); 2465 } 2466 2467 /** 2468 * Test if a post exists, provided its content name. 2469 * @param name the post content name. 2470 * @return true if the post exists. 2471 */ 2472 public boolean hasPostByName(String name) 2473 { 2474 return _postsByName.containsKey(name); 2475 } 2476 2477 /** 2478 * Add a post to the cache. 2479 * @param content the post content. 2480 */ 2481 public void addPost(Content content) 2482 { 2483 CompositeMetadata meta = content.getMetadataHolder(); 2484 2485 Post post = new Post(); 2486 post.setId(content.getId()); 2487 post.setName(content.getName()); 2488 if (meta.hasMetadata("date")) 2489 { 2490 post.setDate(meta.getDate("date")); 2491 } 2492 post.setTags(meta.getStringArray("tags", new String[0])); 2493 2494 _posts.put(post.getId(), post); 2495 _postsByName.put(post.getName(), post); 2496 } 2497 2498 /** 2499 * Add a post to the cache. 2500 * @param post The post 2501 */ 2502 public void addPost(Post post) 2503 { 2504 _posts.put(post.getId(), post); 2505 _postsByName.put(post.getName(), post); 2506 } 2507 2508 /** 2509 * Remove a post from the cache. 2510 * @param content the post content to remove from the cache. 2511 */ 2512 public void removePost(Content content) 2513 { 2514 removePost(content.getId(), content.getName()); 2515 } 2516 2517 /** 2518 * Remove a post from the cache. 2519 * @param id The id of post 2520 * @param name The name of post 2521 */ 2522 public void removePost(String id, String name) 2523 { 2524 _posts.remove(id); 2525 _postsByName.remove(name); 2526 } 2527 2528 /** 2529 * Clear the cache. 2530 */ 2531 public void clear() 2532 { 2533 _posts.clear(); 2534 _postsByName.clear(); 2535 } 2536 } 2537 2538 /** 2539 * Cache of identifier of the future posts. 2540 */ 2541 protected class FuturePostCache 2542 { 2543 /** The posts, indexed by ID. */ 2544 private Map<String, Post> _posts; 2545 2546 /** The posts id, indexed by date. */ 2547 private Multimap<Date, String> _sortedPostIds; 2548 2549 /** 2550 * Construct a AllPostCache instance. 2551 */ 2552 public FuturePostCache() 2553 { 2554 _posts = new HashMap<>(); 2555 _sortedPostIds = TreeMultimap.create(); 2556 } 2557 2558 /** 2559 * Remove the post that are not future anymore (given the current date) 2560 * @return The collection of past posts 2561 */ 2562 public Collection<Post> removePastPosts() 2563 { 2564 Set<Post> pastPosts = new HashSet<>(); 2565 2566 Set<Date> sortedDates = _sortedPostIds.keySet(); 2567 Iterator<Date> iterator = sortedDates.iterator(); 2568 boolean isFuture = false; 2569 Date now = new Date(); 2570 2571 while (iterator.hasNext() && !isFuture) 2572 { 2573 Date date = iterator.next(); 2574 isFuture = date.after(now); 2575 2576 if (!isFuture) 2577 { 2578 Collection<String> pastPostIds = _sortedPostIds.get(date); 2579 for (String id : pastPostIds) 2580 { 2581 Post pastPost = _posts.get(id); 2582 if (pastPost != null) 2583 { 2584 pastPosts.add(pastPost); 2585 } 2586 } 2587 } 2588 } 2589 2590 for (Post pastPost : pastPosts) 2591 { 2592 removePost(pastPost); 2593 } 2594 2595 return pastPosts; 2596 } 2597 2598 /** 2599 * Test if a post exists, provided its ID. 2600 * @param id the post content ID. 2601 * @return true if the post exists. 2602 */ 2603 public Post getPost(String id) 2604 { 2605 return _posts.get(id); 2606 } 2607 2608 /** 2609 * Test if a post exists, provided its ID. 2610 * @param id the post content ID. 2611 * @return true if the post exists. 2612 */ 2613 public boolean hasPost(String id) 2614 { 2615 return _posts.containsKey(id); 2616 } 2617 2618 /** 2619 * Add a post to the cache. 2620 * @param content the post content. 2621 */ 2622 public void addPost(Content content) 2623 { 2624 CompositeMetadata meta = content.getMetadataHolder(); 2625 String id = content.getId(); 2626 2627 if (_posts.containsKey(id)) 2628 { 2629 removePost(content); 2630 } 2631 2632 Date date = meta.getDate("date"); 2633 2634 Post post = new Post(); 2635 post.setId(id); 2636 post.setName(content.getName()); 2637 post.setDate(meta.getDate("date")); 2638 post.setTags(meta.getStringArray("tags", new String[0])); 2639 2640 _posts.put(post.getId(), post); 2641 _sortedPostIds.put(date, id); 2642 } 2643 2644 /** 2645 * Remove a post from the cache. 2646 * @param content the post content to remove from the cache. 2647 */ 2648 public void removePost(Content content) 2649 { 2650 Post post = _posts.remove(content.getId()); 2651 if (post != null) 2652 { 2653 _sortedPostIds.remove(post.getDate(), post.getId()); 2654 } 2655 } 2656 2657 /** 2658 * Remove a post from the cache. 2659 * @param post the post content to remove from the cache. 2660 */ 2661 public void removePost(Post post) 2662 { 2663 _posts.remove(post.getId()); 2664 _sortedPostIds.remove(post.getDate(), post.getId()); 2665 } 2666 2667 /** 2668 * Clear the cache. 2669 */ 2670 public void clear() 2671 { 2672 _posts.clear(); 2673 _sortedPostIds.clear(); 2674 } 2675 } 2676 2677 /** 2678 * Reverse date post comparator. 2679 */ 2680 public class ReverseDatePostComparator implements Comparator<Post> 2681 { 2682 @Override 2683 public int compare(Post p1, Post p2) 2684 { 2685 if (p1.getDate() == null) 2686 { 2687 return 1; 2688 } 2689 if (p2.getDate() == null) 2690 { 2691 return -1; 2692 } 2693 return p2.getDate().compareTo(p1.getDate()); 2694 } 2695 } 2696 2697}