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.ContentLanguageExpression; 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.runtime.i18n.I18nizableText; 062import org.ametys.plugins.repository.query.expression.OrExpression; 063import org.ametys.plugins.repository.query.expression.StringExpression; 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 691 return expr; 692 } 693 694 /** 695 * Get the expression for shared contents 696 * @param currentSiteName the current site name 697 * @return the expression to aware of privacy of contents 698 */ 699 protected Expression getSharedContentsExpression(String currentSiteName) 700 { 701 if (Context.OTHER_SITES.equals(_context) || Context.SITES.equals(_context)) 702 { 703 return SharedContentsHelper.getSharedContentsExpression(_siteManager.getSite(currentSiteName), _siteManager.getSites()); 704 } 705 else if (Context.SITES_LIST.equals(_context)) 706 { 707 List<Site> sites = new ArrayList<>(); 708 for (String siteName : _sites) 709 { 710 sites.add(_siteManager.getSite(siteName)); 711 } 712 713 return SharedContentsHelper.getSharedContentsExpression(_siteManager.getSite(currentSiteName), new CollectionIterable<>(sites)); 714 } 715 716 return null; 717 718 } 719 720 /** 721 * Get the {@link Expression} associated with the given language context 722 * @param lang The current language 723 * @return a {@link Expression} associated with the given language context 724 */ 725 protected Expression getContextLanguagesExpression(String lang) 726 { 727 if (lang == null) 728 { 729 return null; 730 } 731 732 Expression expr = null; 733 734 if (ContextLanguage.CURRENT.equals(_contextLang)) 735 { 736 expr = new ContentLanguageExpression(Operator.EQ, lang); 737 } 738 else if (ContextLanguage.OTHERS.equals(_contextLang)) 739 { 740 expr = new ContentLanguageExpression(Operator.NE, lang); 741 } 742 743 return expr; 744 } 745 746 @Override 747 public String getPageId() 748 { 749 return this._pageId; 750 } 751 752 @Override 753 public void setPageId(String pageId) 754 { 755 this._pageId = pageId; 756 } 757 } 758 759 /** 760 * Compares two contents based on a given list of sort criteria. 761 * In jackrabbit, in ascending order, if the first content does not have the wanted value set, 762 * it's considered to be ordered *before* ("less than") the second content (the JCR spec specifies this behavior as "implementation-defined"). 763 */ 764 protected class ContentComparator implements Comparator<Content> 765 { 766 767 /** The sort criteria. */ 768 protected SortCriteria _sort; 769 770 /** 771 * Build a content comparator from sort criteria. 772 * @param sortCriteria The sort criteria 773 */ 774 public ContentComparator(SortCriteria sortCriteria) 775 { 776 _sort = sortCriteria; 777 } 778 779 @Override 780 public int compare(Content c1, Content c2) 781 { 782 try 783 { 784 for (SortCriterion criterion : _sort.getCriteria()) 785 { 786 boolean ascending = criterion.isAscending(); 787 String metadataPath = criterion.getMetadataPath(); 788 String jcrProperty = criterion.getJcrProperty(); 789 boolean normalize = criterion.isNormalizedSort(); 790 791 int compareAsc = 0; 792 793 if (StringUtils.isNotEmpty(metadataPath)) 794 { 795 compareAsc = compareMetadataAscending(c1, c2, metadataPath, normalize); 796 } 797 else 798 { 799 compareAsc = compareJcrPropertyAscending(c1, c2, jcrProperty, normalize); 800 } 801 802 if (compareAsc != 0) 803 { 804 return ascending ? compareAsc : (0 - compareAsc); 805 } 806 } 807 } 808 catch (RepositoryException e) 809 { 810 _logger.warn("A repository error occurred trying to compare two contents.", e); 811 } 812 catch (AmetysRepositoryException e) 813 { 814 _logger.warn("A repository error occurred trying to compare two contents.", e); 815 } 816 817 return 0; 818 } 819 820 private int compareMetadataAscending(Content c1, Content c2, String metadataPath, boolean normalize) 821 { 822 CompositeMetadata meta1 = c1.getMetadataHolder(); 823 CompositeMetadata meta2 = c2.getMetadataHolder(); 824 825 // Traverse the composites until the one containing the meta. 826 String[] path = StringUtils.split(metadataPath, '/'); 827 for (int i = 0; i < (path.length - 1); i++) 828 { 829 // If the first content does not have a composite, then it's before ("less than") the second. 830 if (!meta1.hasMetadata(path[i])) 831 { 832 return -1; 833 } 834 if (!meta2.hasMetadata(path[i])) 835 { 836 return 1; 837 } 838 839 meta1 = meta1.getCompositeMetadata(path[i]); 840 meta2 = meta2.getCompositeMetadata(path[i]); 841 } 842 843 String metaName = path[path.length - 1]; 844 845 // If the first content does not have the metadata set, then it's before ("less than") the second. 846 if (!meta1.hasMetadata(metaName)) 847 { 848 return -1; 849 } 850 if (!meta2.hasMetadata(metaName)) 851 { 852 return 1; 853 } 854 855 // Compare values depending on theit type. 856 switch (meta1.getType(metaName)) 857 { 858 case STRING: 859 String string1 = meta1.getString(metaName); 860 String string2 = meta2.getString(metaName); 861 return normalize ? string1.compareToIgnoreCase(string2) : string1.compareTo(string2); 862 case DATE: 863 Date date1 = meta1.getDate(metaName); 864 Date date2 = meta2.getDate(metaName); 865 return date1.compareTo(date2); 866 case LONG: 867 Long long1 = meta1.getLong(metaName); 868 Long long2 = meta2.getLong(metaName); 869 return long1.compareTo(long2); 870 case DOUBLE: 871 Double double1 = meta1.getDouble(metaName); 872 Double double2 = meta2.getDouble(metaName); 873 return double1.compareTo(double2); 874 case BINARY: 875 case BOOLEAN: 876 case COMPOSITE: 877 case RICHTEXT: 878 case MULTILINGUAL_STRING: 879 default: 880 // Cannot compare these values, return 0; 881 return 0; 882 } 883 } 884 885 private int compareJcrPropertyAscending(Content c1, Content c2, String jcrProperty, boolean normalize) throws RepositoryException 886 { 887 if (c1 instanceof JCRAmetysObject && c2 instanceof JCRAmetysObject) 888 { 889 Node node1 = ((JCRAmetysObject) c1).getNode(); 890 Node node2 = ((JCRAmetysObject) c2).getNode(); 891 892 // If the first content does not have the property set, then it's before ("less than") the second. 893 if (!node1.hasProperty(jcrProperty)) 894 { 895 return -1; 896 } 897 if (!node2.hasProperty(jcrProperty)) 898 { 899 return 1; 900 } 901 902 Property prop1 = node1.getProperty(jcrProperty); 903 Property prop2 = node2.getProperty(jcrProperty); 904 905 // Compare values depending on theit type. 906 switch (prop1.getType()) 907 { 908 case PropertyType.STRING: 909 String string1 = prop1.getString(); 910 String string2 = prop2.getString(); 911 return normalize ? string1.compareToIgnoreCase(string2) : string1.compareTo(string2); 912 case PropertyType.DATE: 913 Calendar date1 = prop1.getDate(); 914 Calendar date2 = prop2.getDate(); 915 return date1.compareTo(date2); 916 case PropertyType.LONG: 917 Long long1 = prop1.getLong(); 918 Long long2 = prop2.getLong(); 919 return long1.compareTo(long2); 920 case PropertyType.DOUBLE: 921 Double double1 = prop1.getDouble(); 922 Double double2 = prop2.getDouble(); 923 return double1.compareTo(double2); 924 default: 925 // Cannot compare these values, return 0; 926 return 0; 927 } 928 } 929 930 return 0; 931 } 932 933 } 934}