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