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