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.cms.filter; 017 018import java.util.ArrayList; 019import java.util.HashMap; 020import java.util.LinkedHashSet; 021import java.util.List; 022import java.util.Map; 023import java.util.Set; 024 025import org.apache.excalibur.source.SourceResolver; 026 027import org.ametys.cms.contenttype.ContentTypeExtensionPoint; 028import org.ametys.cms.repository.Content; 029import org.ametys.cms.repository.ContentTypeExpression; 030import org.ametys.cms.repository.LanguageExpression; 031import org.ametys.core.util.SizeUtils.ExcludeFromSizeCalculation; 032import org.ametys.plugins.repository.AmetysObjectIterable; 033import org.ametys.plugins.repository.AmetysObjectResolver; 034import org.ametys.plugins.repository.query.SortCriteria; 035import org.ametys.plugins.repository.query.SortCriteria.SortCriterion; 036import org.ametys.plugins.repository.query.expression.AndExpression; 037import org.ametys.plugins.repository.query.expression.Expression; 038import org.ametys.plugins.repository.query.expression.Expression.Operator; 039import org.ametys.plugins.repository.query.expression.MetadataExpression; 040import org.ametys.plugins.repository.query.expression.OrExpression; 041import org.ametys.plugins.repository.query.expression.StringExpression; 042 043/** 044 * This is the default implementation of a {@link ContentFilter}. The filter's property are set by setter function and constructor 045 */ 046public class DefaultContentFilter implements ContentFilter 047{ 048 /** The Ametys object resolver */ 049 @ExcludeFromSizeCalculation 050 protected AmetysObjectResolver _resolver; 051 /** The source resolver */ 052 @ExcludeFromSizeCalculation 053 protected SourceResolver _srcResolver; 054 055 /** The filter id */ 056 protected String _id; 057 /** The list of content types to match */ 058 protected List<String> _contentTypes; 059 /** The list of content languages to match */ 060 protected ContextLanguage _contextLang; 061 /** The metadata to match */ 062 protected Map<String, String> _metadata; 063 /** The metadata condition*/ 064 protected Condition _metadataCondition; 065 /** The number max of results */ 066 protected int _length; 067 /** The view */ 068 protected String _viewName; 069 /** The sort criteria */ 070 protected SortCriteria _sortCriteria; 071 /** The additional expression */ 072 protected Expression _additionalFilterExpression; 073 /** The extension point for content types */ 074 @ExcludeFromSizeCalculation 075 protected ContentTypeExtensionPoint _contentTypeEP; 076 077 /** 078 * Constructor 079 */ 080 public DefaultContentFilter () 081 { 082 // Empty 083 } 084 085 /** 086 * Creates a new filter 087 * @param id The filter unique identifier 088 * @param resolver The ametys object resolver 089 * @param contentTypeExtensionPoint The extension point for content types 090 */ 091 public DefaultContentFilter(String id, AmetysObjectResolver resolver, ContentTypeExtensionPoint contentTypeExtensionPoint) 092 { 093 _id = id; 094 _resolver = resolver; 095 _contentTypeEP = contentTypeExtensionPoint; 096 } 097 098 /** 099 * Creates a new filter from copy of another 100 * @param id The filter unique identifier 101 * @param originalFilter The original filter to be copied 102 * @param resolver The ametys object resolver 103 * @param contentTypeExtensionPoint The extension point for content types 104 */ 105 public DefaultContentFilter(String id, DefaultContentFilter originalFilter, AmetysObjectResolver resolver, ContentTypeExtensionPoint contentTypeExtensionPoint) 106 { 107 _id = id; 108 _resolver = resolver; 109 _contentTypeEP = contentTypeExtensionPoint; 110 _contentTypes = new ArrayList<>(originalFilter._contentTypes); 111 _metadataCondition = originalFilter._metadataCondition; 112 _contextLang = originalFilter._contextLang; 113 _length = originalFilter._length; 114 _viewName = originalFilter._viewName; 115 _metadata = new HashMap<>(originalFilter._metadata); 116 117 SortCriteria originalSC = originalFilter._sortCriteria; 118 if (originalSC != null) 119 { 120 SortCriteria sortCriteria = new SortCriteria(); 121 for (SortCriterion criterion : originalFilter.getSortCriteria().getCriteria()) 122 { 123 if (criterion.getMetadataPath() != null) 124 { 125 sortCriteria.addCriterion(criterion.getMetadataPath(), criterion.isAscending(), criterion.isNormalizedSort()); 126 } 127 else if (criterion.getJcrProperty() != null) 128 { 129 sortCriteria.addCriterion(criterion.getJcrProperty(), criterion.isAscending(), criterion.isNormalizedSort()); 130 } 131 } 132 _sortCriteria = sortCriteria; 133 } 134 135 // FIXME The filter expressions should be copied here but it missing a Expression.clone() or Expression.copy() method to do it. 136 // For now the filter expression is not used when this constructor is used, so we do nothing for now since filters will be refactored soon (for 4.3 ?) 137 } 138 139 @Override 140 public List<String> getContentTypes() 141 { 142 return _contentTypes; 143 } 144 145 @Override 146 public Map<String, String> getMetadataValues() 147 { 148 return _metadata; 149 } 150 151 @Override 152 public Condition getMetadataCondition() 153 { 154 return _metadataCondition; 155 } 156 157 @Override 158 public ContextLanguage getContextLanguage() 159 { 160 return _contextLang; 161 } 162 163 @Override 164 public String getId() 165 { 166 return _id; 167 } 168 169 @Override 170 public int getLength() 171 { 172 return _length; 173 } 174 175 @Override 176 public String getView() 177 { 178 return _viewName; 179 } 180 181 @Override 182 public SortCriteria getSortCriteria() 183 { 184 return _sortCriteria; 185 } 186 187 @Override 188 public void addContentType(String cTypeId) 189 { 190 if (_contentTypes == null) 191 { 192 _contentTypes = new ArrayList<>(); 193 } 194 _contentTypes.add(cTypeId); 195 } 196 197 @Override 198 public void addMetadata(String metadataId, String value) 199 { 200 if (_metadata == null) 201 { 202 _metadata = new HashMap<>(); 203 } 204 _metadata.put(metadataId, value); 205 } 206 207 @Override 208 public void setMetadataCondition(Condition condition) 209 { 210 _metadataCondition = condition; 211 } 212 213 @Override 214 public Expression getAdditionalFilterExpression() 215 { 216 return _additionalFilterExpression; 217 } 218 219 @Override 220 public void setAdditionalFilterExpression(Expression expression) 221 { 222 _additionalFilterExpression = expression; 223 } 224 225 @Override 226 public void setContextLanguage(ContextLanguage context) 227 { 228 _contextLang = context; 229 } 230 231 @Override 232 public void setId(String id) 233 { 234 _id = id; 235 } 236 237 @Override 238 public void setResolver (AmetysObjectResolver resolver) 239 { 240 _resolver = resolver; 241 } 242 243 244 @Override 245 public void setLength(int length) 246 { 247 _length = length; 248 } 249 250 251 @Override 252 public void setView(String viewName) 253 { 254 _viewName = viewName; 255 } 256 257 @Override 258 public void addSortCriteria(String metadataId, boolean ascending, boolean useLowerCase) 259 { 260 if (_sortCriteria == null) 261 { 262 _sortCriteria = new SortCriteria(); 263 } 264 _sortCriteria.addCriterion(metadataId, ascending, useLowerCase); 265 } 266 267 @Override 268 public AmetysObjectIterable<Content> getMatchingContents() 269 { 270 String xpathQuery = getXPathQuery(); 271 AmetysObjectIterable<Content> contents = _resolver.query(xpathQuery); 272 273 return contents; 274 } 275 276 @Override 277 public AmetysObjectIterable<Content> getMatchingContents(String lang) 278 { 279 String xpathQuery = getXPathQuery(lang); 280 AmetysObjectIterable<Content> contents = _resolver.query(xpathQuery); 281 282 return contents; 283 } 284 285 /** 286 * Creates the XPath query corresponding to this filter. 287 * @return the created XPath query 288 */ 289 public String getXPathQuery () 290 { 291 Expression expr = getFilterExpression (); 292 return org.ametys.plugins.repository.query.QueryHelper.getXPathQuery(null, "ametys:content", expr, _sortCriteria); 293 } 294 295 /** 296 * Creates the XPath query corresponding to this filter. 297 * @param lang The current language 298 * @return the created XPath query 299 */ 300 public String getXPathQuery (String lang) 301 { 302 Expression expr = getFilterExpression (); 303 Expression langExpr = getContextLanguagesExpression(lang); 304 expr = langExpr != null ? new AndExpression(expr, langExpr) : expr; 305 306 return org.ametys.plugins.repository.query.QueryHelper.getXPathQuery(null, "ametys:content", expr, _sortCriteria); 307 308 } 309 310 /** 311 * Get the expression corresponding to this filter 312 * @return The created expression to match contents 313 */ 314 protected Expression getFilterExpression () 315 { 316 Expression expr = getContentTypesExpression (); 317 Expression metadataExpr = getMetadataExpression(); 318 Expression additionalExpr = getAdditionalFilterExpression(); 319 320 return new AndExpression(expr, metadataExpr, additionalExpr); 321 } 322 323 /** 324 * Get the expression corresponding to the filter's content types 325 * @return The expression corresponding to the filter's content types 326 */ 327 protected Expression getContentTypesExpression () 328 { 329 Expression expr = null; 330 331 List<Expression> cTypesExpr = new ArrayList<>(); 332 if (_contentTypes != null && _contentTypes.size() > 0) 333 { 334 for (String contentType : _contentTypes) 335 { 336 // Filter on content type and all its children 337 Set<String> cTypeAndSubTypes = new LinkedHashSet<>(); 338 cTypeAndSubTypes.add(contentType); 339 cTypeAndSubTypes.addAll(_contentTypeEP.getSubTypes(contentType)); 340 cTypesExpr.add(new ContentTypeExpression(Operator.EQ, cTypeAndSubTypes.toArray(new String[cTypeAndSubTypes.size()]))); 341 } 342 343 expr = new OrExpression(cTypesExpr.toArray(new Expression[cTypesExpr.size()])); 344 } 345 346 return expr; 347 } 348 349 /** 350 * Get the {@link Expression} associated with the given language context 351 * @param lang The current language 352 * @return a {@link Expression} associated with the given language context 353 */ 354 protected Expression getContextLanguagesExpression (String lang) 355 { 356 if (lang == null) 357 { 358 return null; 359 } 360 361 Expression expr = null; 362 363 if (ContextLanguage.CURRENT.equals(_contextLang)) 364 { 365 expr = new LanguageExpression(Operator.EQ, lang); 366 } 367 else if (ContextLanguage.OTHERS.equals(_contextLang)) 368 { 369 expr = new LanguageExpression(Operator.NE, lang); 370 } 371 372 return expr; 373 } 374 375 /** 376 * Get the expression corresponding to the filter's tags 377 * @return The expression corresponding to the filter's tags 378 */ 379 protected Expression getMetadataExpression () 380 { 381 Expression expr = null; 382 383 List<Expression> metadataExpr = new ArrayList<>(); 384 385 if (_metadata != null && _metadata.size() > 0) 386 { 387 for (String metadataId : _metadata.keySet()) 388 { 389 String value = _metadata.get(metadataId); 390 metadataExpr.add(value != null ? new StringExpression(metadataId, Operator.EQ, value) : new MetadataExpression(metadataId)); 391 } 392 393 if (_metadataCondition == Condition.OR) 394 { 395 expr = new OrExpression(metadataExpr.toArray(new Expression[metadataExpr.size()])); 396 } 397 else 398 { 399 expr = new AndExpression(metadataExpr.toArray(new Expression[metadataExpr.size()])); 400 } 401 } 402 403 return expr; 404 } 405} 406 407