001/* 002 * Copyright 2015 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.search.systemprop; 017 018import java.util.ArrayList; 019import java.util.Collection; 020import java.util.Collections; 021import java.util.Date; 022import java.util.List; 023import java.util.Map; 024 025import org.apache.avalon.framework.configuration.Configurable; 026import org.apache.avalon.framework.configuration.Configuration; 027import org.apache.avalon.framework.configuration.ConfigurationException; 028import org.apache.avalon.framework.service.ServiceException; 029import org.apache.avalon.framework.service.ServiceManager; 030import org.apache.avalon.framework.service.Serviceable; 031import org.apache.solr.common.SolrInputDocument; 032 033import org.ametys.cms.content.indexing.solr.SolrIndexer; 034import org.ametys.cms.repository.Content; 035import org.ametys.cms.search.SearchField; 036import org.ametys.cms.search.model.SystemProperty; 037import org.ametys.cms.search.solr.schema.CopyFieldDefinition; 038import org.ametys.cms.search.solr.schema.FieldDefinition; 039import org.ametys.cms.search.solr.schema.SchemaDefinition; 040import org.ametys.cms.search.solr.schema.SchemaHelper; 041import org.ametys.core.user.UserIdentity; 042import org.ametys.core.util.I18nUtils; 043import org.ametys.runtime.i18n.I18nizableText; 044import org.ametys.runtime.parameter.ParameterHelper; 045import org.ametys.runtime.parameter.ParameterHelper.ParameterType; 046import org.ametys.runtime.plugin.component.AbstractLogEnabled; 047import org.ametys.runtime.plugin.component.PluginAware; 048 049/** 050 * Abstract class providing standard behavior and helper methods for a 051 * {@link SystemProperty}. 052 */ 053public abstract class AbstractSystemProperty extends AbstractLogEnabled implements SystemProperty, Serviceable, Configurable, PluginAware 054{ 055 056 /** The i18n utils. */ 057 protected I18nUtils _i18nUtils; 058 059 /** The property ID. */ 060 protected String _id; 061 062 /** The property label. */ 063 protected I18nizableText _label; 064 065 /** The property description. */ 066 protected I18nizableText _description; 067 068 /** The property plugin name. */ 069 protected String _pluginName; 070 071 @Override 072 public void setPluginInfo(String pluginName, String featureName, String id) 073 { 074 _pluginName = pluginName; 075 _id = id; 076 } 077 078 public void service(ServiceManager manager) throws ServiceException 079 { 080 _i18nUtils = (I18nUtils) manager.lookup(I18nUtils.ROLE); 081 } 082 083 @Override 084 public void configure(Configuration configuration) throws ConfigurationException 085 { 086 _label = _parseI18nizableText(configuration, _pluginName, "label"); 087 _description = _parseI18nizableText(configuration, _pluginName, "description"); 088 } 089 090 @Override 091 public String getId() 092 { 093 return _id; 094 } 095 096 @Override 097 public I18nizableText getLabel() 098 { 099 return _label; 100 } 101 102 @Override 103 public I18nizableText getDescription() 104 { 105 return _description; 106 } 107 108 @Override 109 public EnumeratorDefinition getEnumeratorDefinition(Collection<String> contentTypes, Configuration configuration) 110 { 111 // Default null, override to set an enumerator. 112 return null; 113 } 114 115 @Override 116 public String getContentTypeId() 117 { 118 return null; 119 } 120 121 @Override 122 public boolean isSearchable() 123 { 124 // Default to true: override when the property is not searchable. 125 return true; 126 } 127 128 @Override 129 public boolean isDisplayable() 130 { 131 // Default to true: override when the property is not displayable. 132 return true; 133 } 134 135 @Override 136 public String getWidget() 137 { 138 return null; 139 } 140 141 public Map<String, I18nizableText> getWidgetParameters() 142 { 143 return Collections.emptyMap(); 144 } 145 146 @Override 147 public String getRenderer() 148 { 149 return null; 150 } 151 152 @Override 153 public String getConverter() 154 { 155 return null; 156 } 157 158 @Override 159 public Integer getColumnWidth() 160 { 161 return null; 162 } 163 164 @Override 165 public void index(Content content, SolrInputDocument document) 166 { 167 SearchField searchField = getSearchField(); 168 String fieldName = searchField != null ? searchField.getName() : null; 169 String sortFieldName = searchField != null ? searchField.getSortField() : null; 170 Object value = getValue(content); 171 172 if (value != null && fieldName != null) 173 { 174 if (isMultiple()) 175 { 176 if (value instanceof Collection< ? > || value.getClass().isArray()) 177 { 178 // FIXME can lead to indexation mistakes if collection or array of dates 179 document.setField(fieldName, value); 180 } 181 else 182 { 183 _indexSingleValue(document, fieldName, value); 184 } 185 } 186 else 187 { 188 _indexSingleValue(document, fieldName, value); 189 } 190 191 Object sortValue = getSortValue(content); 192 if (isSortable() && sortValue != null && sortFieldName != null && !fieldName.equals(sortFieldName)) 193 { 194 document.setField(sortFieldName, sortValue); 195 } 196 } 197 } 198 199 private void _indexSingleValue(SolrInputDocument document, String fieldName, Object value) 200 { 201 if (value instanceof Date) 202 { 203 document.setField(fieldName, SolrIndexer.dateFormat().format((Date) value)); 204 } 205 else 206 { 207 // Index the single value. 208 document.setField(fieldName, value); 209 } 210 } 211 212 @Override 213 public Object getFullValue(Content content) 214 { 215 // Default to getValue(), override to provide a specific "full" value. 216 return getValue(content); 217 } 218 219 @Override 220 public Object getSortValue(Content content) 221 { 222 // Default to getValue(), override to provide a specific sort value. 223 return null; 224 } 225 226 @Override 227 public Collection<SchemaDefinition> getSchemaDefinitions() 228 { 229 List<SchemaDefinition> definitions = new ArrayList<>(); 230 231 SearchField searchField = getSearchField(); 232 if (searchField != null) 233 { 234 String name = searchField.getName(); 235 String sortFieldName = searchField.getSortField(); 236 String facetFieldName = searchField.getFacetField(); 237 boolean multiple = isMultiple(); 238 String type = SchemaHelper.getSchemaType(getType()); 239 240 if (type != null) 241 { 242 definitions.add(new FieldDefinition(name, type, multiple, false)); 243 244 if (sortFieldName != null && !sortFieldName.equals(name)) 245 { 246 definitions.add(new FieldDefinition(sortFieldName, type, false, false)); 247 } 248 249 if (facetFieldName != null && !facetFieldName.equals(name)) 250 { 251 definitions.add(new FieldDefinition(facetFieldName, type, multiple, true)); 252 definitions.add(new CopyFieldDefinition(name, facetFieldName)); 253 } 254 } 255 } 256 257 return definitions; 258 } 259 260 /** 261 * Parse an i18n text. 262 * 263 * @param config the configuration to use. 264 * @param pluginName the current plugin name. 265 * @param name the child name. 266 * @return the i18n text. 267 * @throws ConfigurationException if the configuration is not valid. 268 */ 269 protected I18nizableText _parseI18nizableText(Configuration config, String pluginName, String name) throws ConfigurationException 270 { 271 return I18nizableText.parseI18nizableText(config.getChild(name), "plugin." + pluginName); 272 } 273 274 /** 275 * Get the value as a long. 276 * 277 * @param value The value as an object. 278 * @return The value as a long. 279 */ 280 protected String parseString(Object value) 281 { 282 if (value instanceof String) 283 { 284 return (String) value; 285 } 286 else 287 { 288 throw new IllegalArgumentException("The value " + value + " for criterion " + getId() + " is not a valid String value."); 289 } 290 } 291 292 /** 293 * Get the value as a long. 294 * 295 * @param value The value as an object. 296 * @return The value as a long. 297 */ 298 protected long parseLong(Object value) 299 { 300 if (value instanceof Long) 301 { 302 return (Long) value; 303 } 304 else if (value instanceof String) 305 { 306 return Long.parseLong((String) value); 307 } 308 else 309 { 310 throw new IllegalArgumentException("The value " + value + " for criterion " + getId() + " is not a valid long value."); 311 } 312 } 313 314 /** 315 * Get the value as a double. 316 * 317 * @param value The value as an object. 318 * @return The value as a double. 319 */ 320 protected double parseDouble(Object value) 321 { 322 if (value instanceof Double) 323 { 324 return (Double) value; 325 } 326 else if (value instanceof String) 327 { 328 return Double.parseDouble((String) value); 329 } 330 else 331 { 332 throw new IllegalArgumentException("The value " + value + " for criterion " + getId() + " is not a valid double value."); 333 } 334 } 335 336 /** 337 * Get the value as a boolean. 338 * 339 * @param value The value as an object, can be a Boolean or a String. 340 * @return The value as a boolean. 341 */ 342 protected boolean parseBoolean(Object value) 343 { 344 if (value instanceof Boolean) 345 { 346 return (Boolean) value; 347 } 348 else if (value instanceof String) 349 { 350 return Boolean.parseBoolean((String) value); 351 } 352 else 353 { 354 throw new IllegalArgumentException("The value " + value + " for criterion " + getId() + " is not a valid boolean value."); 355 } 356 } 357 358 /** 359 * Get the value as a date. 360 * 361 * @param value The value as an object, can be a Date or a String. 362 * @return The value as a Date object. 363 */ 364 protected Date parseDate(Object value) 365 { 366 if (value instanceof Date) 367 { 368 return (Date) value; 369 } 370 else if (value instanceof String) 371 { 372 return (Date) ParameterHelper.castValue((String) value, ParameterType.DATE); 373 } 374 else 375 { 376 throw new IllegalArgumentException("The value " + value + " for criterion " + getId() + " is not a valid date value."); 377 } 378 } 379 380 /** 381 * Get the value as array of {@link UserIdentity} 382 * @param value The value to parse 383 * @return A array of {@link UserIdentity} 384 */ 385 @SuppressWarnings("unchecked") 386 protected UserIdentity[] parseUserArray (Object value) 387 { 388 if (value instanceof UserIdentity) 389 { 390 return new UserIdentity[] {(UserIdentity) value}; 391 } 392 else if (value instanceof UserIdentity[]) 393 { 394 return (UserIdentity[]) value; 395 } 396 else if (value instanceof List<?>) 397 { 398 return (UserIdentity[]) ((List<?>) value).stream().map(v -> new UserIdentity(((Map<String, String>) v).get("login"), ((Map<String, String>) v).get("populationId"))).toArray(); 399 } 400 else if (value instanceof Map) 401 { 402 Map<String, Object> userValue = (Map<String, Object>) value; 403 404 String login = (String) userValue.get("login"); 405 String populationId = (String) userValue.get("populationId"); 406 407 if (login != null && populationId != null) 408 { 409 return new UserIdentity[] {new UserIdentity(login, populationId)}; 410 } 411 return new UserIdentity[0]; 412 } 413 else 414 { 415 throw new IllegalArgumentException("The value " + value + " for criterion " + getId() + " is not a valid UserIdentity values."); 416 } 417 } 418 419 /** 420 * Get the value as a array of String 421 * @param value The value as an object to parse 422 * @return The values as a String array 423 */ 424 protected String[] parseStringArray(Object value) 425 { 426 if (value instanceof String) 427 { 428 return new String[] {(String) value}; 429 } 430 else if (value instanceof String[]) 431 { 432 return (String[]) value; 433 } 434 else if (value instanceof List<?>) 435 { 436 return ((List<?>) value).stream() 437 .map(v -> String.valueOf(v)) 438 .toArray(String[]::new); 439 } 440 else 441 { 442 throw new IllegalArgumentException("The value " + value + " for criterion " + getId() + " is not a valid String[] values."); 443 } 444 } 445 446}