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