001/* 002 * Copyright 2010 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.web.filter; 017 018import java.util.ArrayList; 019import java.util.Calendar; 020import java.util.Collection; 021import java.util.Collections; 022import java.util.Comparator; 023import java.util.Date; 024import java.util.HashMap; 025import java.util.List; 026import java.util.Map; 027import java.util.Set; 028 029import javax.jcr.Node; 030import javax.jcr.Property; 031import javax.jcr.PropertyType; 032import javax.jcr.RepositoryException; 033 034import org.apache.commons.lang.StringUtils; 035import org.apache.jackrabbit.util.ISO9075; 036import org.slf4j.Logger; 037import org.slf4j.LoggerFactory; 038 039import org.ametys.cms.contenttype.ContentTypeExtensionPoint; 040import org.ametys.cms.filter.DefaultContentFilter; 041import org.ametys.cms.repository.Content; 042import org.ametys.cms.repository.LanguageExpression; 043import org.ametys.cms.tag.Tag; 044import org.ametys.cms.tag.TagHelper; 045import org.ametys.cms.tag.TagProviderExtensionPoint; 046import org.ametys.plugins.repository.AmetysObjectIterable; 047import org.ametys.plugins.repository.AmetysObjectResolver; 048import org.ametys.plugins.repository.AmetysRepositoryException; 049import org.ametys.plugins.repository.ChainedAmetysObjectIterable; 050import org.ametys.plugins.repository.CollatingUniqueAmetysObjectIterable; 051import org.ametys.plugins.repository.CollectionIterable; 052import org.ametys.plugins.repository.EmptyIterable; 053import org.ametys.plugins.repository.UnknownAmetysObjectException; 054import org.ametys.plugins.repository.jcr.JCRAmetysObject; 055import org.ametys.plugins.repository.metadata.CompositeMetadata; 056import org.ametys.plugins.repository.query.SortCriteria; 057import org.ametys.plugins.repository.query.SortCriteria.SortCriterion; 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.NotExpression; 063import org.ametys.plugins.repository.query.expression.OrExpression; 064import org.ametys.plugins.repository.query.expression.StringExpression; 065import org.ametys.runtime.i18n.I18nizableText; 066import org.ametys.web.repository.content.jcr.DefaultWebContent; 067import org.ametys.web.repository.page.Page; 068import org.ametys.web.repository.site.Site; 069import org.ametys.web.repository.site.SiteManager; 070import org.ametys.web.tags.TagExpression; 071import org.ametys.web.tags.TagExpression.LogicalOperator; 072 073/** 074 * This is the default implementation of a {@link WebContentFilter}. The filter's property are set by setter function and constructor 075 */ 076public class DefaultWebContentFilter extends DefaultContentFilter implements WebContentFilter 077{ 078 /** The search contexts. */ 079 protected List<FilterSearchContext> _searchContexts; 080 081 /** The mask orphan contents property */ 082 protected boolean _maskOrphan; 083 /** The access limitation */ 084 protected AccessLimitation _accessLimitation; 085 /** The title */ 086 protected I18nizableText _title; 087 /** The description */ 088 protected I18nizableText _description; 089 /** The logger */ 090 protected Logger _logger = LoggerFactory.getLogger(this.getClass()); 091 /** The site manager */ 092 protected SiteManager _siteManager; 093 /** The tag provider */ 094 protected TagProviderExtensionPoint _tagProviderEP; 095 096 /** 097 * Constructor 098 */ 099 public DefaultWebContentFilter () 100 { 101 // Empty 102 _searchContexts = new ArrayList<>(); 103 } 104 105 /** 106 * Creates a new filter 107 * @param id The filter unique identifier 108 * @param resolver The ametys object resolver 109 * @param contentTypeExtensionPoint The extension point for content types 110 * @param siteManager The site manager 111 * @param tagProviderEP The tag provider 112 */ 113 public DefaultWebContentFilter(String id, AmetysObjectResolver resolver, ContentTypeExtensionPoint contentTypeExtensionPoint, SiteManager siteManager, TagProviderExtensionPoint tagProviderEP) 114 { 115 super(id, resolver, contentTypeExtensionPoint); 116 _siteManager = siteManager; 117 _tagProviderEP = tagProviderEP; 118 _searchContexts = new ArrayList<>(); 119 } 120 121 /** 122 * Creates a new filter from copy of another 123 * @param id The filter unique identifier 124 * @param originalFilter The original filter to be copied 125 * @param resolver The ametys object resolver 126 * @param contentTypeExtensionPoint The extension point for content types 127 * @param siteManager The site manager 128 * @param tagProviderEP The tag provider 129 */ 130 public DefaultWebContentFilter(String id, DefaultWebContentFilter originalFilter, AmetysObjectResolver resolver, ContentTypeExtensionPoint contentTypeExtensionPoint, SiteManager siteManager, TagProviderExtensionPoint tagProviderEP) 131 { 132 super(id, originalFilter, resolver, contentTypeExtensionPoint); 133 _siteManager = siteManager; 134 _tagProviderEP = tagProviderEP; 135 _searchContexts = new ArrayList<>(originalFilter._searchContexts); 136 _title = originalFilter._title; 137 _description = originalFilter._description; 138 _maskOrphan = originalFilter._maskOrphan; 139 _accessLimitation = originalFilter._accessLimitation; 140 } 141 142 @Override 143 public I18nizableText getTitle() 144 { 145 return _title; 146 } 147 148 @Override 149 public void setTitle(I18nizableText title) 150 { 151 _title = title; 152 } 153 154 @Override 155 public I18nizableText getDescription() 156 { 157 return _description; 158 } 159 160 @Override 161 public void setDescription(I18nizableText description) 162 { 163 _description = description; 164 } 165 166 @Override 167 public ContextLanguage getContextLanguage() 168 { 169 throw new UnsupportedOperationException("This implementation use multiple context languages, use getSearchContexts instead."); 170 } 171 172 @Override 173 public void setContextLanguage(ContextLanguage context) 174 { 175 throw new UnsupportedOperationException("This implementation use multiple context languages, use setSearchContexts instead."); 176 } 177 178 @Override 179 public void addMetadata(String metadataId, String value) 180 { 181 if (_metadata == null) 182 { 183 _metadata = new HashMap<>(); 184 } 185 _metadata.put(metadataId, value); 186 } 187 188 @Override 189 public List<FilterSearchContext> getSearchContexts() 190 { 191 return Collections.unmodifiableList(_searchContexts); 192 } 193 194 @Override 195 public FilterSearchContext addSearchContext() 196 { 197 FilterSearchContext context = createSeachContext(); 198 _searchContexts.add(context); 199 return context; 200 } 201 202 /** 203 * Create a search context. 204 * @return the created search context. 205 */ 206 protected FilterSearchContext createSeachContext() 207 { 208 return new DefaultFilterSearchContext(_siteManager); 209 } 210 211 @Override 212 public void setMaskOrphanContents(boolean mask) 213 { 214 _maskOrphan = mask; 215 } 216 217 @Override 218 public boolean maskOrphanContents() 219 { 220 return _maskOrphan; 221 } 222 223 @Override 224 public AccessLimitation getAccessLimitation() 225 { 226 // Default to PAGE_ACCESS. 227 return _accessLimitation == null ? AccessLimitation.PAGE_ACCESS : _accessLimitation; 228 } 229 230 @Override 231 public void setAccessLimitation(AccessLimitation limitation) 232 { 233 _accessLimitation = limitation; 234 } 235 236 @Override 237 public AmetysObjectIterable<Content> getMatchingContents(String siteName, String lang, Page page) 238 { 239 List<AmetysObjectIterable<Content>> iterables = new ArrayList<>(); 240 241 for (FilterSearchContext context : _searchContexts) 242 { 243 AmetysObjectIterable<Content> contents = getMatchingContents(siteName, lang, page, context); 244 if (contents != null) 245 { 246 iterables.add(contents); 247 } 248 } 249 250 AmetysObjectIterable<Content> contents = null; 251 252 if (!iterables.isEmpty()) 253 { 254 Comparator<Content> comparator = new ContentComparator(_sortCriteria); 255 contents = new CollatingUniqueAmetysObjectIterable<>(iterables, comparator); 256 } 257 else 258 { 259 if (_logger.isInfoEnabled()) 260 { 261 _logger.info("The filter '" + _id + "' has a null content collection"); 262 } 263 contents = new EmptyIterable<>(); 264 } 265 266 return contents; 267 } 268 269 /** 270 * Get the matching contents for the given search context. 271 * @param siteName the site name. 272 * @param lang the language. 273 * @param page the context page. 274 * @param filterContext the search context. 275 * @return An iterable over matching Contents. 276 */ 277 protected AmetysObjectIterable<Content> getMatchingContents(String siteName, String lang, Page page, FilterSearchContext filterContext) 278 { 279 AmetysObjectIterable<Content> contents = null; 280 281 Context context = filterContext.getContext(); 282 int depth = filterContext.getDepth(); 283 284 Page parentPage = page; 285 if (filterContext.getPageId() != null) 286 { 287 parentPage = _resolver.resolveById(filterContext.getPageId()); 288 try 289 { 290 parentPage = _resolver.resolveById(filterContext.getPageId()); 291 } 292 catch (UnknownAmetysObjectException e) 293 { 294 _logger.warn("the page '" + filterContext.getPageId() + "' can not be found for content's filter of id '" + _id + "'"); 295 Collection<Content> emptyList = Collections.emptyList(); 296 return new CollectionIterable<>(emptyList); 297 } 298 } 299 String xpathQuery = null; 300 if (parentPage == null && context.equals(Context.CHILD_PAGES)) 301 { 302 // Leave null. 303 _logger.warn("The current page can not be null for content's filter of id '" + _id + "'"); 304 } 305 else if (context.equals(Context.CHILD_PAGES) && (depth == 0 || depth == 1)) 306 { 307 xpathQuery = getXPathQuery(siteName, parentPage, depth, filterContext); 308 contents = _resolver.query(xpathQuery); 309 } 310 else if (context.equals(Context.CHILD_PAGES)) 311 { 312 List<AmetysObjectIterable<Content>> itList = new ArrayList<>(); 313 for (int i = 1; i <= depth; i++) 314 { 315 xpathQuery = getXPathQuery(siteName, parentPage, i, filterContext); 316 AmetysObjectIterable<Content> it = _resolver.query(xpathQuery); 317 itList.add(it); 318 } 319 contents = new ChainedAmetysObjectIterable<>(itList); 320 } 321 else 322 { 323 xpathQuery = getXPathQuery(siteName, lang, filterContext); 324 contents = _resolver.query(xpathQuery); 325 } 326 327 return contents; 328 } 329 330 /** 331 * Creates the XPath query corresponding to this filter. 332 * @param siteName The current site name 333 * @param lang The current language 334 * @param filterContext the filter search context. 335 * @return the created XPath query 336 */ 337 public String getXPathQuery(String siteName, String lang, FilterSearchContext filterContext) 338 { 339 List<Expression> exprs = new ArrayList<>(); 340 341 Expression filterExpr = getFilterExpression(); 342 if (filterExpr != null) 343 { 344 exprs.add(filterExpr); 345 } 346 347 Expression contextExpr = filterContext.getFullExpression(siteName, lang); 348 if (contextExpr != null) 349 { 350 exprs.add(contextExpr); 351 } 352 353 Expression expr = exprs.size() > 0 ? new AndExpression(exprs.toArray(new Expression[exprs.size()])) : null; 354 355 return org.ametys.plugins.repository.query.QueryHelper.getXPathQuery(null, "ametys:content", expr, _sortCriteria); 356 } 357 358 /** 359 * Creates the XPath query corresponding to specified {@link Page}, {@link Expression} and {@link SortCriteria}. 360 * @param siteName The current site name 361 * @param page The page where to start the search 362 * @param depth the search depth 363 * @param filterContext the filter search context. 364 * @return the created XPath query 365 */ 366 protected String getXPathQuery(String siteName, Page page, int depth, FilterSearchContext filterContext) 367 { 368 String depthPath = ""; 369 if (depth == 0) 370 { 371 depthPath = "/"; 372 } 373 else if (depth == 1) 374 { 375 depthPath = ""; 376 } 377 else 378 { 379 for (int i = 0; i < depth; i++) 380 { 381 depthPath += "/*"; 382 } 383 } 384 385 // Build the content expression 386 Expression expr = getFilterExpression(siteName, filterContext); 387 388 String xpath = "//element(" + page.getSite().getName() + ", ametys:site)/ametys-internal:sitemaps/" + _encode(page.getSitemap().getName()) 389 + "/" + page.getPathInSitemap() 390 + depthPath 391 + "/element(*, ametys:page)/ametys-internal:zones/*/ametys-internal:zoneItems/*/jcr:deref(@ametys-internal:content, '*')" + (expr != null ? "[" + expr.build() + "]" : "") 392 + (_sortCriteria != null ? (" " + _sortCriteria.build()) : ""); 393 return xpath; 394 395 } 396 397 /** 398 * Get the filter expression for a given search context. 399 * @param siteName The current site name 400 * @param filterContext the filter search context. 401 * @return the filter expression. 402 */ 403 protected Expression getFilterExpression(String siteName, FilterSearchContext filterContext) 404 { 405 Expression expr = super.getFilterExpression(); 406 407 Expression tagExpr = filterContext.getTagsExpression(siteName); 408 if (tagExpr != null) 409 { 410 expr = expr != null ? new AndExpression(expr, tagExpr) : tagExpr; 411 } 412 413 return expr; 414 } 415 416 private static String _encode(String path) 417 { 418 if (path == null || path.length() == 0) 419 { 420 return ""; 421 } 422 423 int pos = path.indexOf("/"); 424 if (pos == -1) 425 { 426 return ISO9075.encode(path); 427 } 428 else 429 { 430 return ISO9075.encode(path.substring(0, pos)) + "/" + _encode(path.substring(pos + 1)); 431 } 432 } 433 434 /** 435 * Default implementation of a filter search context. 436 */ 437 public class DefaultFilterSearchContext implements FilterSearchContext 438 { 439 /** The tags. */ 440 protected List<String> _tags; 441 /** The tags condition*/ 442 protected Condition _tagsCondition; 443 /** The tags auto posting */ 444 protected boolean _tagsAutoPosting; 445 /** The context for search */ 446 protected Context _context; 447 /** The list of sites to match*/ 448 protected List<String> _sites; 449 /** The search depth */ 450 protected int _depth; 451 /** The list of content languages to match */ 452 @SuppressWarnings("hiding") 453 protected ContextLanguage _contextLang; 454 /** The parent page Id */ 455 protected String _pageId; 456 /** The list of content languages to match */ 457 @SuppressWarnings("hiding") 458 protected SiteManager _siteManager; 459 460 /** 461 * Build a DefaultFilterSearchContext. 462 * @param siteManager the site manager. 463 */ 464 public DefaultFilterSearchContext(SiteManager siteManager) 465 { 466 _siteManager = siteManager; 467 _tags = new ArrayList<>(); 468 _sites = new ArrayList<>(); 469 } 470 471 @Override 472 public int getDepth() 473 { 474 return _depth; 475 } 476 477 @Override 478 public List<String> getTags() 479 { 480 return _tags; 481 } 482 483 @Override 484 public Condition getTagsCondition () 485 { 486 return _tagsCondition; 487 } 488 489 @Override 490 public boolean getTagsAutoPosting() 491 { 492 return _tagsAutoPosting; 493 } 494 495 @Override 496 public Context getContext() 497 { 498 return _context; 499 } 500 501 @Override 502 public List<String> getSites() 503 { 504 return _sites; 505 } 506 507 @Override 508 public void addTag(String tag) 509 { 510 _tags.add(tag); 511 } 512 513 @Override 514 public void setTagsCondition(Condition condition) 515 { 516 _tagsCondition = condition; 517 } 518 519 @Override 520 public void setTagsAutoPosting(boolean enable) 521 { 522 _tagsAutoPosting = enable; 523 } 524 525 @Override 526 public void setContext(Context context) 527 { 528 _context = context; 529 } 530 531 @Override 532 public void addSite(String siteName) 533 { 534 _sites.add(siteName); 535 } 536 537 @Override 538 public void setDepth(int depth) 539 { 540 _depth = depth; 541 } 542 543 @Override 544 public ContextLanguage getContextLanguage() 545 { 546 return _contextLang; 547 } 548 549 @Override 550 public void setContextLanguage(ContextLanguage language) 551 { 552 _contextLang = language; 553 } 554 555 @Override 556 public Expression getFullExpression(String siteName, String language) 557 { 558 List<Expression> exprs = new ArrayList<>(); 559 560 Expression contextExpr = getContextExpression(siteName); 561 if (contextExpr != null) 562 { 563 exprs.add(contextExpr); 564 } 565 566 Expression langExpr = getContextLanguagesExpression(language); 567 if (langExpr != null) 568 { 569 exprs.add(langExpr); 570 } 571 572 Expression sharedExpr = getSharedContentsExpression(siteName); 573 if (sharedExpr != null) 574 { 575 exprs.add(sharedExpr); 576 } 577 578 Expression tagExpr = getTagsExpression(siteName); 579 if (tagExpr != null) 580 { 581 exprs.add(tagExpr); 582 } 583 584 Expression expr = exprs.size() > 0 ? new AndExpression(exprs.toArray(new Expression[exprs.size()])) : null; 585 586 return expr; 587 } 588 589 @Override 590 public Expression getTagsExpression(String siteName) 591 { 592 if (_tags != null && _tags.size() > 0) 593 { 594 List<Expression> tagsExpr = new ArrayList<>(); 595 for (String tagName : _tags) 596 { 597 if (_tagsAutoPosting) 598 { 599 Tag tag = _getTag(siteName, tagName); 600 if (tag != null) 601 { 602 Set<String> descendantNames = TagHelper.getDescendantNames(tag, true); 603 tagsExpr.add(new TagExpression(Operator.EQ, descendantNames.toArray(new String[descendantNames.size()]), LogicalOperator.OR)); 604 } 605 } 606 else 607 { 608 tagsExpr.add(new TagExpression(Operator.EQ, tagName)); 609 } 610 } 611 612 if (_tagsCondition == Condition.OR) 613 { 614 return new OrExpression(tagsExpr.toArray(new Expression[tagsExpr.size()])); 615 } 616 else 617 { 618 return new AndExpression(tagsExpr.toArray(new Expression[tagsExpr.size()])); 619 } 620 } 621 622 return null; 623 } 624 625 /** 626 * Internal tag getter given the search context 627 * @param currentSiteName The name of the current site 628 * @param tagName the name of the tag 629 * @return The tag or null 630 */ 631 protected Tag _getTag(String currentSiteName, String tagName) 632 { 633 Map<String, Object> parameters = new HashMap<>(); 634 635 Tag tag = null; 636 637 switch (_context) 638 { 639 case CURRENT_SITE: 640 case CHILD_PAGES: 641 parameters.put("siteName", currentSiteName); 642 tag = _tagProviderEP.getTag(tagName, parameters); 643 break; 644 case SITES_LIST: 645 if (_sites != null && _sites.size() == 1) 646 { 647 parameters.put("siteName", _sites.get(0)); 648 tag = _tagProviderEP.getTag(tagName, parameters); 649 } 650 break; 651 case SITES: 652 case OTHER_SITES: 653 tag = _tagProviderEP.getTag(tagName, null); 654 break; 655 default: 656 // nothing 657 break; 658 } 659 660 return tag; 661 } 662 663 /** 664 * Get the {@link Expression} associated with the given site context 665 * @param siteName The current site name 666 * @return a {@link Expression} associated with the given site context 667 */ 668 protected Expression getContextExpression(String siteName) 669 { 670 Expression expr = null; 671 672 if (Context.CURRENT_SITE.equals(_context)) 673 { 674 expr = new StringExpression(DefaultWebContent.METADATA_SITE, Operator.EQ, siteName); 675 } 676 else if (Context.OTHER_SITES.equals(_context)) 677 { 678 expr = new StringExpression(DefaultWebContent.METADATA_SITE, Operator.NE, siteName); 679 } 680 else if (Context.SITES_LIST.equals(_context)) 681 { 682 List<Expression> sitesExpr = new ArrayList<>(); 683 if (_sites != null && _sites.size() > 0) 684 { 685 for (String site : _sites) 686 { 687 sitesExpr.add(new StringExpression(DefaultWebContent.METADATA_SITE, Operator.EQ, site)); 688 } 689 } 690 expr = new OrExpression(sitesExpr.toArray(new Expression[sitesExpr.size()])); 691 } 692 else if (Context.NO_SITE.equals(_context)) 693 { 694 expr = new NotExpression(new MetadataExpression(DefaultWebContent.METADATA_SITE)); 695 } 696 697 return expr; 698 } 699 700 /** 701 * Get the expression for shared contents 702 * @param currentSiteName the current site name 703 * @return the expression to aware of privacy of contents 704 */ 705 protected Expression getSharedContentsExpression(String currentSiteName) 706 { 707 if (Context.OTHER_SITES.equals(_context) || Context.SITES.equals(_context)) 708 { 709 return SharedContentsHelper.getSharedContentsExpression(_siteManager.getSite(currentSiteName), _siteManager.getSites()); 710 } 711 else if (Context.SITES_LIST.equals(_context)) 712 { 713 List<Site> sites = new ArrayList<>(); 714 for (String siteName : _sites) 715 { 716 sites.add(_siteManager.getSite(siteName)); 717 } 718 719 return SharedContentsHelper.getSharedContentsExpression(_siteManager.getSite(currentSiteName), new CollectionIterable<>(sites)); 720 } 721 722 return null; 723 724 } 725 726 /** 727 * Get the {@link Expression} associated with the given language context 728 * @param lang The current language 729 * @return a {@link Expression} associated with the given language context 730 */ 731 protected Expression getContextLanguagesExpression(String lang) 732 { 733 if (lang == null) 734 { 735 return null; 736 } 737 738 Expression expr = null; 739 740 if (ContextLanguage.CURRENT.equals(_contextLang)) 741 { 742 expr = new LanguageExpression(Operator.EQ, lang); 743 } 744 else if (ContextLanguage.OTHERS.equals(_contextLang)) 745 { 746 expr = new LanguageExpression(Operator.NE, lang); 747 } 748 749 return expr; 750 } 751 752 @Override 753 public String getPageId() 754 { 755 return this._pageId; 756 } 757 758 @Override 759 public void setPageId(String pageId) 760 { 761 this._pageId = pageId; 762 } 763 } 764 765 /** 766 * Compares two contents based on a given list of sort criteria. 767 * In jackrabbit, in ascending order, if the first content does not have the wanted value set, 768 * it's considered to be ordered *before* ("less than") the second content (the JCR spec specifies this behavior as "implementation-defined"). 769 */ 770 protected class ContentComparator implements Comparator<Content> 771 { 772 773 /** The sort criteria. */ 774 protected SortCriteria _sort; 775 776 /** 777 * Build a content comparator from sort criteria. 778 * @param sortCriteria The sort criteria 779 */ 780 public ContentComparator(SortCriteria sortCriteria) 781 { 782 _sort = sortCriteria; 783 } 784 785 @Override 786 public int compare(Content c1, Content c2) 787 { 788 try 789 { 790 for (SortCriterion criterion : _sort.getCriteria()) 791 { 792 boolean ascending = criterion.isAscending(); 793 String metadataPath = criterion.getMetadataPath(); 794 String jcrProperty = criterion.getJcrProperty(); 795 boolean normalize = criterion.isNormalizedSort(); 796 797 int compareAsc = 0; 798 799 if (StringUtils.isNotEmpty(metadataPath)) 800 { 801 compareAsc = compareMetadataAscending(c1, c2, metadataPath, normalize); 802 } 803 else 804 { 805 compareAsc = compareJcrPropertyAscending(c1, c2, jcrProperty, normalize); 806 } 807 808 if (compareAsc != 0) 809 { 810 return ascending ? compareAsc : (0 - compareAsc); 811 } 812 } 813 } 814 catch (RepositoryException e) 815 { 816 _logger.warn("A repository error occurred trying to compare two contents.", e); 817 } 818 catch (AmetysRepositoryException e) 819 { 820 _logger.warn("A repository error occurred trying to compare two contents.", e); 821 } 822 823 return 0; 824 } 825 826 private int compareMetadataAscending(Content c1, Content c2, String metadataPath, boolean normalize) 827 { 828 CompositeMetadata meta1 = c1.getMetadataHolder(); 829 CompositeMetadata meta2 = c2.getMetadataHolder(); 830 831 // Traverse the composites until the one containing the meta. 832 String[] path = StringUtils.split(metadataPath, '/'); 833 for (int i = 0; i < (path.length - 1); i++) 834 { 835 // If the first content does not have a composite, then it's before ("less than") the second. 836 if (!meta1.hasMetadata(path[i])) 837 { 838 return -1; 839 } 840 if (!meta2.hasMetadata(path[i])) 841 { 842 return 1; 843 } 844 845 meta1 = meta1.getCompositeMetadata(path[i]); 846 meta2 = meta2.getCompositeMetadata(path[i]); 847 } 848 849 String metaName = path[path.length - 1]; 850 851 // If the first content does not have the metadata set, then it's before ("less than") the second. 852 if (!meta1.hasMetadata(metaName)) 853 { 854 return -1; 855 } 856 if (!meta2.hasMetadata(metaName)) 857 { 858 return 1; 859 } 860 861 // Compare values depending on theit type. 862 switch (meta1.getType(metaName)) 863 { 864 case STRING: 865 String string1 = meta1.getString(metaName); 866 String string2 = meta2.getString(metaName); 867 return normalize ? string1.compareToIgnoreCase(string2) : string1.compareTo(string2); 868 case DATE: 869 Date date1 = meta1.getDate(metaName); 870 Date date2 = meta2.getDate(metaName); 871 return date1.compareTo(date2); 872 case LONG: 873 Long long1 = meta1.getLong(metaName); 874 Long long2 = meta2.getLong(metaName); 875 return long1.compareTo(long2); 876 case DOUBLE: 877 Double double1 = meta1.getDouble(metaName); 878 Double double2 = meta2.getDouble(metaName); 879 return double1.compareTo(double2); 880 case BINARY: 881 case BOOLEAN: 882 case COMPOSITE: 883 case RICHTEXT: 884 case MULTILINGUAL_STRING: 885 default: 886 // Cannot compare these values, return 0; 887 return 0; 888 } 889 } 890 891 private int compareJcrPropertyAscending(Content c1, Content c2, String jcrProperty, boolean normalize) throws RepositoryException 892 { 893 if (c1 instanceof JCRAmetysObject && c2 instanceof JCRAmetysObject) 894 { 895 Node node1 = ((JCRAmetysObject) c1).getNode(); 896 Node node2 = ((JCRAmetysObject) c2).getNode(); 897 898 // If the first content does not have the property set, then it's before ("less than") the second. 899 if (!node1.hasProperty(jcrProperty)) 900 { 901 return -1; 902 } 903 if (!node2.hasProperty(jcrProperty)) 904 { 905 return 1; 906 } 907 908 Property prop1 = node1.getProperty(jcrProperty); 909 Property prop2 = node2.getProperty(jcrProperty); 910 911 // Compare values depending on theit type. 912 switch (prop1.getType()) 913 { 914 case PropertyType.STRING: 915 String string1 = prop1.getString(); 916 String string2 = prop2.getString(); 917 return normalize ? string1.compareToIgnoreCase(string2) : string1.compareTo(string2); 918 case PropertyType.DATE: 919 Calendar date1 = prop1.getDate(); 920 Calendar date2 = prop2.getDate(); 921 return date1.compareTo(date2); 922 case PropertyType.LONG: 923 Long long1 = prop1.getLong(); 924 Long long2 = prop2.getLong(); 925 return long1.compareTo(long2); 926 case PropertyType.DOUBLE: 927 Double double1 = prop1.getDouble(); 928 Double double2 = prop2.getDouble(); 929 return double1.compareTo(double2); 930 default: 931 // Cannot compare these values, return 0; 932 return 0; 933 } 934 } 935 936 return 0; 937 } 938 939 } 940}