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.List; 021import java.util.Map; 022 023import org.apache.avalon.framework.service.ServiceException; 024import org.apache.avalon.framework.service.ServiceManager; 025import org.apache.avalon.framework.service.Serviceable; 026import org.apache.solr.common.SolrInputDocument; 027 028import org.ametys.cms.data.ametysobject.ModelAwareDataAwareAmetysObject; 029import org.ametys.cms.data.type.indexing.IndexableElementType; 030import org.ametys.cms.model.properties.AbstractProperty; 031import org.ametys.cms.search.SearchField; 032import org.ametys.cms.search.model.SystemProperty; 033import org.ametys.cms.search.solr.schema.CopyFieldDefinition; 034import org.ametys.cms.search.solr.schema.FieldDefinition; 035import org.ametys.cms.search.solr.schema.SchemaDefinition; 036import org.ametys.core.user.UserIdentity; 037import org.ametys.core.user.UserManager; 038import org.ametys.core.util.I18nUtils; 039import org.ametys.core.util.date.AdaptableDate; 040import org.ametys.core.util.date.AdaptableDateParser; 041import org.ametys.plugins.core.user.UserHelper; 042import org.ametys.plugins.repository.AmetysObjectResolver; 043import org.ametys.runtime.model.type.DataContext; 044 045/** 046 * Abstract class providing standard behavior and helper methods for a {@link SystemProperty}. 047 * @param <T> type of the property values 048 * @param <X> type of ametys object supported by this property 049 */ 050public abstract class AbstractSystemProperty<T, X extends ModelAwareDataAwareAmetysObject> extends AbstractProperty<T, X> implements SystemProperty<T, X>, Serviceable 051{ 052 /** The i18n utils. */ 053 protected I18nUtils _i18nUtils; 054 /** The user helper */ 055 protected UserHelper _userHelper; 056 /** The ametys object resolver */ 057 protected AmetysObjectResolver _resolver; 058 /** The user manager */ 059 protected UserManager _userManager; 060 /** The service manager */ 061 protected ServiceManager _manager; 062 063 public void service(ServiceManager manager) throws ServiceException 064 { 065 _manager = manager; 066 _i18nUtils = (I18nUtils) manager.lookup(I18nUtils.ROLE); 067 _userHelper = (UserHelper) manager.lookup(UserHelper.ROLE); 068 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 069 _userManager = (UserManager) manager.lookup(UserManager.ROLE); 070 } 071 072 @Override 073 protected String _getNameConfigurationAttribute() 074 { 075 return "id"; 076 } 077 078 @Override 079 public void indexValue(SolrInputDocument document, X ametysObject, DataContext context) 080 { 081 Object value = getValue(ametysObject); 082 083 SearchField searchField = getSearchField(); 084 085 if (value == null || searchField == null || searchField.getName() == null) 086 { 087 // Nothing to index 088 return; 089 } 090 091 IndexableElementType<T> type = getType(); 092 if (type.getManagedClass().isInstance(value)) 093 { 094 Object valueToIndex = type.getSingleValueToIndex(type.getManagedClass().cast(value)); 095 document.addField(searchField.getName(), valueToIndex); 096 } 097 else if (type.getManagedClassArray().isInstance(value)) 098 { 099 T[] values = type.getManagedClassArray().cast(value); 100 for (T singleValue : values) 101 { 102 Object valueToIndex = type.getSingleValueToIndex(singleValue); 103 document.addField(searchField.getName(), valueToIndex); 104 } 105 } 106 107 // Index sort field 108 Object sortValue = getSortValue(ametysObject); 109 if (isSortable() && sortValue != null && searchField.getSortField() != null && !searchField.getName().equals(searchField.getSortField())) 110 { 111 document.setField(searchField.getSortField(), sortValue); 112 } 113 } 114 115 @Override 116 public Object getSortValue(X ametysObject) 117 { 118 // Default to getValue(), override to provide a specific sort value. 119 Object value = getValue(ametysObject); 120 121 if (value == null) 122 { 123 return null; 124 } 125 126 if (isMultiple() && value instanceof Object[] && ((Object[]) value).length > 0) 127 { 128 return ((Object[]) value)[0]; // return the first value 129 } 130 else 131 { 132 return value; 133 } 134 } 135 136 @Override 137 public Collection<SchemaDefinition> getSchemaDefinitions() 138 { 139 List<SchemaDefinition> definitions = new ArrayList<>(); 140 141 SearchField searchField = getSearchField(); 142 if (searchField != null) 143 { 144 String name = searchField.getName(); 145 String sortFieldName = searchField.getSortField(); 146 String facetFieldName = searchField.getFacetField(); 147 boolean multiple = isMultiple(); 148 String type = getType().getSchemaType(); 149 150 if (type != null) 151 { 152 definitions.add(new FieldDefinition(name, type, multiple, false)); 153 154 if (sortFieldName != null && !sortFieldName.equals(name)) 155 { 156 definitions.add(new FieldDefinition(sortFieldName, type, false, false)); 157 } 158 159 if (facetFieldName != null && !facetFieldName.equals(name)) 160 { 161 // By default the index value in field name will be automatically copy in facet field 162 // So we do not need the index facet field manually 163 definitions.add(new FieldDefinition(facetFieldName, type, multiple, true)); 164 definitions.add(new CopyFieldDefinition(name, facetFieldName)); 165 } 166 } 167 } 168 169 return definitions; 170 } 171 172 /** 173 * Get the value as a long. 174 * 175 * @param value The value as an object. 176 * @return The value as a long. 177 */ 178 protected String parseString(Object value) 179 { 180 if (value instanceof String) 181 { 182 return (String) value; 183 } 184 else 185 { 186 throw new IllegalArgumentException("The value " + value + " for criterion " + getName() + " is not a valid String value."); 187 } 188 } 189 190 /** 191 * Get the value as a long. 192 * 193 * @param value The value as an object. 194 * @return The value as a long. 195 */ 196 protected long parseLong(Object value) 197 { 198 if (value instanceof Number) 199 { 200 return ((Number) value).longValue(); 201 } 202 else if (value instanceof String) 203 { 204 return Long.parseLong((String) value); 205 } 206 else 207 { 208 throw new IllegalArgumentException("The value " + value + " for criterion " + getName() + " is not a valid long value."); 209 } 210 } 211 212 /** 213 * Get the value as a double. 214 * 215 * @param value The value as an object. 216 * @return The value as a double. 217 */ 218 protected double parseDouble(Object value) 219 { 220 if (value instanceof Number) 221 { 222 return ((Number) value).doubleValue(); 223 } 224 else if (value instanceof String) 225 { 226 return Double.parseDouble((String) value); 227 } 228 else 229 { 230 throw new IllegalArgumentException("The value " + value + " for criterion " + getName() + " is not a valid double value."); 231 } 232 } 233 234 /** 235 * Get the value as a boolean. 236 * 237 * @param value The value as an object, can be a Boolean or a String. 238 * @return The value as a boolean. 239 */ 240 protected boolean parseBoolean(Object value) 241 { 242 if (value instanceof Boolean) 243 { 244 return (Boolean) value; 245 } 246 else if (value instanceof String) 247 { 248 return Boolean.parseBoolean((String) value); 249 } 250 else 251 { 252 throw new IllegalArgumentException("The value " + value + " for criterion " + getName() + " is not a valid boolean value."); 253 } 254 } 255 256 /** 257 * Get the value as a date. 258 * 259 * @param value The value as an object, can be a Date or a String. 260 * @return The value as a Date object. 261 */ 262 protected AdaptableDate parseDate(Object value) 263 { 264 if (value instanceof AdaptableDate) 265 { 266 return (AdaptableDate) value; 267 } 268 else if (value instanceof String) 269 { 270 return AdaptableDateParser.parse((String) value); 271 } 272 else 273 { 274 throw new IllegalArgumentException("The value " + value + " for criterion " + getName() + " is not a valid date value."); 275 } 276 } 277 278 /** 279 * Get the value as array of {@link UserIdentity} 280 * @param value The value to parse 281 * @return A array of {@link UserIdentity} 282 */ 283 @SuppressWarnings("unchecked") 284 protected UserIdentity[] parseUserArray (Object value) 285 { 286 if (value instanceof UserIdentity) 287 { 288 return new UserIdentity[] {(UserIdentity) value}; 289 } 290 else if (value instanceof UserIdentity[]) 291 { 292 return (UserIdentity[]) value; 293 } 294 else if (value instanceof List<?>) 295 { 296 return (UserIdentity[]) ((List<?>) value).stream().map(v -> _userHelper.json2userIdentity((Map<String, String>) v)).toArray(); 297 } 298 else if (value instanceof Map) 299 { 300 UserIdentity userIdentity = _userHelper.json2userIdentity((Map<String, String>) value); 301 if (userIdentity != null) 302 { 303 return new UserIdentity[] {userIdentity}; 304 } 305 return new UserIdentity[0]; 306 } 307 else 308 { 309 throw new IllegalArgumentException("The value " + value + " for criterion " + getName() + " is not a valid UserIdentity values."); 310 } 311 } 312 313 /** 314 * Get the value as a array of String 315 * @param value The value as an object to parse 316 * @return The values as a String array 317 */ 318 protected String[] parseStringArray(Object value) 319 { 320 if (value instanceof String) 321 { 322 return new String[] {(String) value}; 323 } 324 else if (value instanceof String[]) 325 { 326 return (String[]) value; 327 } 328 else if (value instanceof List<?>) 329 { 330 return ((List<?>) value).stream() 331 .map(v -> String.valueOf(v)) 332 .toArray(String[]::new); 333 } 334 else 335 { 336 throw new IllegalArgumentException("The value " + value + " for criterion " + getName() + " is not a valid String[] values."); 337 } 338 } 339}