001/* 002 * Copyright 2018 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.runtime.model; 017 018import java.util.ArrayList; 019import java.util.Collections; 020import java.util.HashMap; 021import java.util.HashSet; 022import java.util.List; 023import java.util.Map; 024import java.util.Objects; 025import java.util.Set; 026 027import org.apache.avalon.framework.component.Component; 028import org.apache.avalon.framework.context.Context; 029import org.apache.avalon.framework.service.ServiceException; 030import org.apache.avalon.framework.service.ServiceManager; 031import org.apache.cocoon.xml.AttributesImpl; 032import org.xml.sax.ContentHandler; 033import org.xml.sax.SAXException; 034 035import org.ametys.core.model.ModelItemHelper; 036import org.ametys.core.util.XMLUtils; 037import org.ametys.runtime.i18n.I18nizableText; 038import org.ametys.runtime.model.checker.ItemCheckerDescriptor; 039import org.ametys.runtime.model.disableconditions.DisableCondition; 040import org.ametys.runtime.model.disableconditions.DisableConditions; 041 042/** 043 * Abstract class for model items 044 */ 045public abstract class AbstractModelItem implements ModelItem 046{ 047 /** The service manager */ 048 protected static ServiceManager __serviceManager; 049 050 /** The cocoon context */ 051 protected static Context __context; 052 053 private ModelItemHelper _modelItemHelper; 054 055 private String _name; 056 private String _pluginName; 057 private I18nizableText _label; 058 private I18nizableText _description; 059 private Set<ItemCheckerDescriptor> _itemCheckers = new HashSet<>(); 060 private String _widget; 061 private Map<String, I18nizableText> _widgetParams = new HashMap<>(); 062 private DisableConditions _disableConditions; 063 064 private Model _model; 065 private ModelItemGroup _parent; 066 067 private String _path; 068 069 /** 070 * Default constructor. 071 */ 072 public AbstractModelItem() 073 { 074 // Empty constructor 075 } 076 077 /** 078 * Constructor used to create simple models and items 079 * @param name the name of the item 080 */ 081 public AbstractModelItem(String name) 082 { 083 _name = name; 084 } 085 086 /** 087 * Constructor by copying an existing {@link AbstractModelItem}. 088 * @param modelItemToCopy The {@link AbstractModelItem} to copy 089 */ 090 public AbstractModelItem(ModelItem modelItemToCopy) 091 { 092 setName(modelItemToCopy.getName()); 093 setPluginName(modelItemToCopy.getPluginName()); 094 setLabel(modelItemToCopy.getLabel()); 095 setDescription(modelItemToCopy.getDescription()); 096 for (ItemCheckerDescriptor itemChecker : modelItemToCopy.getItemCheckers()) 097 { 098 addItemChecker(itemChecker); 099 } 100 setModel(modelItemToCopy.getModel()); 101 setParent(modelItemToCopy.getParent()); 102 103 // Widget 104 setWidget(modelItemToCopy.getWidget()); 105 setWidgetParameters(modelItemToCopy.getWidgetParameters()); 106 107 setDisableConditions(modelItemToCopy.getDisableConditions()); 108 } 109 110 public String getName() 111 { 112 return _name; 113 } 114 115 public void setName(String name) 116 { 117 _name = name; 118 _path = null; 119 } 120 121 public String getPluginName() 122 { 123 return _pluginName; 124 } 125 126 public void setPluginName(String pluginName) 127 { 128 _pluginName = pluginName; 129 } 130 131 public I18nizableText getLabel() 132 { 133 return _label; 134 } 135 136 public void setLabel(I18nizableText label) 137 { 138 _label = label; 139 } 140 141 public I18nizableText getDescription() 142 { 143 return _description; 144 } 145 146 public void setDescription(I18nizableText description) 147 { 148 _description = description; 149 } 150 151 public void addItemChecker(ItemCheckerDescriptor itemChecker) 152 { 153 _itemCheckers.add(itemChecker); 154 } 155 156 public Set<ItemCheckerDescriptor> getItemCheckers() 157 { 158 return Collections.unmodifiableSet(_itemCheckers); 159 } 160 161 162 public String getWidget() 163 { 164 return _widget; 165 } 166 167 public void setWidget(String widget) 168 { 169 _widget = widget; 170 } 171 172 public Map<String, I18nizableText> getWidgetParameters() 173 { 174 return _widgetParams; 175 } 176 177 public void setWidgetParameters (Map<String, I18nizableText> params) 178 { 179 _widgetParams = params; 180 } 181 182 public DisableConditions getDisableConditions() 183 { 184 return _disableConditions; 185 } 186 187 public void setDisableConditions(DisableConditions disableConditions) 188 { 189 _disableConditions = disableConditions; 190 } 191 192 public String getPath() 193 { 194 if (_path != null) 195 { 196 return _path; 197 } 198 199 if (getName() == null) 200 { 201 return null; 202 } 203 204 StringBuilder path = new StringBuilder(); 205 206 ModelItemGroup parent = getParent(); 207 if (parent != null && parent.getPath() != null) 208 { 209 path.append(parent.getPath()).append(ITEM_PATH_SEPARATOR); 210 } 211 212 path.append(getName()); 213 _path = path.toString(); 214 return _path; 215 } 216 217 public Model getModel() 218 { 219 return _model; 220 } 221 222 public void setModel(Model model) 223 { 224 _model = model; 225 } 226 227 public ModelItemGroup getParent() 228 { 229 return _parent; 230 } 231 232 public void setParent(ModelItemGroup parent) 233 { 234 _parent = parent; 235 } 236 237 public Map<String, Object> toJSON(DefinitionContext context) 238 { 239 if (_shouldJSONBeEmpty(context)) 240 { 241 return Map.of(); 242 } 243 else 244 { 245 return _toJSON(context); 246 } 247 } 248 249 /** 250 * Converts the model item in a JSON map 251 * @param context the context of the definition 252 * @return The model item as a JSON map 253 */ 254 protected Map<String, Object> _toJSON(DefinitionContext context) 255 { 256 Map<String, Object> result = new HashMap<>(); 257 258 result.put("name", getName()); 259 result.put("plugin", getPluginName()); 260 result.put("label", getLabel()); 261 result.put("description", getDescription()); 262 result.put("path", getPath()); 263 264 if (!getItemCheckers().isEmpty()) 265 { 266 List<Map<String, Object>> checkers2json = new ArrayList<>(); 267 for (ItemCheckerDescriptor paramChecker : getItemCheckers()) 268 { 269 checkers2json.add(paramChecker.toJSON()); 270 } 271 272 result.put("field-checker", checkers2json); 273 } 274 275 result.putAll(_widgetToJSON(context)); 276 277 if (ModelHelper.hasDisableConditions(this)) 278 { 279 result.put("disableCondition", disableConditionsToJSON()); 280 } 281 282 return result; 283 } 284 285 /** 286 * Converts the model item's widget in a JSON map 287 * @param context the context of the definition 288 * @return The model item's widget as a JSON map 289 */ 290 protected Map<String, Object> _widgetToJSON(DefinitionContext context) 291 { 292 Map<String, Object> result = new HashMap<>(); 293 294 result.put("widget", getWidget()); 295 296 Map<String, I18nizableText> widgetParameters = getWidgetParameters(); 297 if (widgetParameters != null && !widgetParameters.isEmpty()) 298 { 299 result.put("widget-params", widgetParameters); 300 } 301 302 return result; 303 } 304 305 /** 306 * Converts the definition's disable conditions in a JSON map 307 * @return The definition's disable conditions as a JSON map 308 */ 309 protected Map<String, Object> disableConditionsToJSON() 310 { 311 return _getModelItemHelper().disableConditionsToJSON(this); 312 } 313 314 /** 315 * Checks if the current definition JSON conversion should return an empty map 316 * @param context the context of the definition 317 * @return <code>true</code> if the JSON conversion should return an empty map, <code>false</code> otherwise 318 */ 319 protected boolean _shouldJSONBeEmpty(DefinitionContext context) 320 { 321 return false; 322 } 323 324 @SuppressWarnings("static-access") 325 public void toSAX(ContentHandler contentHandler, DefinitionContext context) throws SAXException 326 { 327 if (!getItemCheckers().isEmpty()) 328 { 329 for (ItemCheckerDescriptor paramChecker : getItemCheckers()) 330 { 331 XMLUtils.startElement(contentHandler, "field-checker"); 332 paramChecker.toSAX(contentHandler); 333 XMLUtils.endElement(contentHandler, "field-checker"); 334 } 335 } 336 337 _widgetToSAX(contentHandler, context); 338 339 if (getDisableConditions() != null) 340 { 341 _disableConditionsToSAX(contentHandler, getDisableConditions()); 342 } 343 } 344 345 /** 346 * Generates SAX events for the model item's widget 347 * @param contentHandler the {@link ContentHandler} that will receive the SAX events 348 * @param context the context of the definition 349 * @throws SAXException if an error occurs during the SAX events generation 350 */ 351 @SuppressWarnings("static-access") 352 protected void _widgetToSAX(ContentHandler contentHandler, DefinitionContext context) throws SAXException 353 { 354 XMLUtils.createElementIfNotNull(contentHandler, "widget", getWidget()); 355 356 Map<String, I18nizableText> widgetParameters = getWidgetParameters(); 357 if (widgetParameters != null && !widgetParameters.isEmpty()) 358 { 359 XMLUtils.startElement(contentHandler, "widget-params"); 360 361 for (Map.Entry<String, I18nizableText> param : widgetParameters.entrySet()) 362 { 363 _widgetParameterToSAX(contentHandler, param.getKey(), param.getValue(), context); 364 } 365 366 XMLUtils.endElement(contentHandler, "widget-params"); 367 } 368 } 369 370 /** 371 * Generates SAX events for the given widget parameter 372 * @param contentHandler the {@link ContentHandler} that will receive the SAX events 373 * @param parameterName the name of the parameter 374 * @param parameterValue the value of the parameter 375 * @param context the context of the definition 376 * @throws SAXException if an error occurs during the SAX events generation 377 */ 378 @SuppressWarnings("static-access") 379 protected void _widgetParameterToSAX(ContentHandler contentHandler, String parameterName, I18nizableText parameterValue, DefinitionContext context) throws SAXException 380 { 381 AttributesImpl paramAttributes = new AttributesImpl(); 382 paramAttributes.addCDATAAttribute("name", parameterName); 383 384 XMLUtils.startElement(contentHandler, "param", paramAttributes); 385 parameterValue.toSAX(contentHandler); 386 XMLUtils.endElement(contentHandler, "param"); 387 } 388 389 @SuppressWarnings("static-access") 390 private void _disableConditionsToSAX(ContentHandler contentHandler, DisableConditions disableConditions) throws SAXException 391 { 392 AttributesImpl attributes = new AttributesImpl(); 393 attributes.addCDATAAttribute("type", disableConditions.getAssociationType().toString().toLowerCase()); 394 XMLUtils.startElement(contentHandler, "disable-conditions", attributes); 395 396 // Handle simple conditions 397 XMLUtils.startElement(contentHandler, "conditions"); 398 for (DisableCondition disableCondition : disableConditions.getConditions()) 399 { 400 _disableConditionToSAX(contentHandler, disableCondition); 401 } 402 XMLUtils.endElement(contentHandler, "conditions"); 403 404 // Handle nested conditions 405 XMLUtils.startElement(contentHandler, "nested-conditions"); 406 for (DisableConditions subDisableConditions : disableConditions.getSubConditions()) 407 { 408 _disableConditionsToSAX(contentHandler, subDisableConditions); 409 } 410 XMLUtils.endElement(contentHandler, "nested-conditions"); 411 412 XMLUtils.endElement(contentHandler, "disable-conditions"); 413 } 414 415 @SuppressWarnings("static-access") 416 private void _disableConditionToSAX(ContentHandler contentHandler, DisableCondition disableCondition) throws SAXException 417 { 418 AttributesImpl attributes = new AttributesImpl(); 419 attributes.addCDATAAttribute("id", disableCondition.getId()); 420 attributes.addCDATAAttribute("operator", disableCondition.getOperator().toString().toLowerCase()); 421 422 XMLUtils.createElement(contentHandler, "condition", attributes, disableCondition.getValue()); 423 } 424 425 public int compareTo(ModelItem item) 426 { 427 if (item == null) 428 { 429 return 1; 430 } 431 432 String name = getName(); 433 if (name != null) 434 { 435 return name.compareTo(item.getName()); 436 } 437 438 I18nizableText label = getLabel(); 439 if (label != null) 440 { 441 I18nizableText otherLabel = item.getLabel(); 442 if (otherLabel == null) 443 { 444 return 1; 445 } 446 447 return label.toString().compareTo(otherLabel.toString()); 448 } 449 450 return 0; 451 } 452 453 @Override 454 public boolean equals(Object obj) 455 { 456 if (obj == null || !getClass().equals(obj.getClass())) 457 { 458 return false; 459 } 460 461 ModelItem item = (ModelItem) obj; 462 463 // The item's model can be null 464 if (getModel() != item.getModel()) 465 { 466 if (getModel() == null ^ item.getModel() == null) 467 { 468 return false; 469 } 470 471 if (!Objects.equals(getModel().getFamilyId(), item.getModel().getFamilyId()) || !Objects.equals(getModel().getId(), item.getModel().getId())) 472 { 473 return false; 474 } 475 } 476 477 if (getPath() != null || item.getPath() != null) 478 { 479 return Objects.equals(getPath(), item.getPath()); 480 } 481 482 if (getLabel() != null || item.getLabel() != null) 483 { 484 return Objects.equals(getLabel(), item.getLabel()); 485 } 486 487 return false; 488 } 489 490 @Override 491 public int hashCode() 492 { 493 if (getPath() != null) 494 { 495 return getPath().hashCode(); 496 } 497 498 return getLabel().hashCode(); 499 } 500 501 @Override 502 public String toString() 503 { 504 if (getPath() != null) 505 { 506 return getPath(); 507 } 508 509 return getLabel().toString(); 510 } 511 512 /** 513 * Retrieves the {@link ModelItemHelper} 514 * @return the {@link ModelItemHelper} 515 */ 516 protected ModelItemHelper _getModelItemHelper() 517 { 518 if (_modelItemHelper == null) 519 { 520 try 521 { 522 _modelItemHelper = (ModelItemHelper) __serviceManager.lookup(ModelItemHelper.ROLE); 523 } 524 catch (ServiceException e) 525 { 526 throw new RuntimeException("Unable to lookup after the model item helper", e); 527 } 528 } 529 530 return _modelItemHelper; 531 } 532 533 /** 534 * Set the service manager 535 * The {@link ServiceManager} is used in the model items creation methods to get the model item type. 536 * {@link ModelItem} is not a {@link Component} and can't have a {@link ServiceManager} itself. Another {@link Component} has to set it 537 * @param manager the service manager to set 538 */ 539 public static void setServiceManager(ServiceManager manager) 540 { 541 __serviceManager = manager; 542 } 543 544 /** 545 * Set the cocoon context 546 * {@link ModelItem} is not a {@link Component} and can't have a {@link Context} itself. Another {@link Component} has to set it 547 * @param context the cocoon context to set 548 */ 549 public static void setContext(Context context) 550 { 551 __context = context; 552 } 553}