001/* 002 * Copyright 2023 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.Collection; 019import java.util.List; 020import java.util.Optional; 021import java.util.regex.Matcher; 022import java.util.regex.Pattern; 023 024import org.apache.avalon.framework.configuration.Configuration; 025import org.apache.avalon.framework.configuration.ConfigurationException; 026import org.apache.avalon.framework.logger.AbstractLogEnabled; 027import org.apache.commons.lang3.StringUtils; 028 029import org.ametys.runtime.i18n.I18nizableText; 030import org.ametys.runtime.model.ItemParserHelper.ConfigurationAndPluginName; 031import org.ametys.runtime.model.ModelViewItem.DisabledItemRendering; 032import org.ametys.runtime.model.ViewHelper.InsertMode; 033import org.ametys.runtime.model.exception.UndefinedItemPathException; 034 035/** 036 * Abstract component that parses view's configuration 037 */ 038public abstract class AbstractViewParser extends AbstractLogEnabled implements ViewParser 039{ 040 /** The regex to remove view references in model item references */ 041 protected static final String __VIEW_REFERENCE_REGEX = "\\[(.+)\\]"; 042 /** The pattern to find view references in model item references */ 043 protected static final Pattern __VIEW_REFERENCE_PATTERN = Pattern.compile("^[^\\[]+" + __VIEW_REFERENCE_REGEX + "$"); 044 045 public View parseView(ConfigurationAndPluginName viewConfiguration, ViewParserContext context) throws ConfigurationException 046 { 047 View view = new View(); 048 view.setName(_parseViewName(viewConfiguration)); 049 050 // Get the model 051 Collection<? extends Model> model = _getModel(); 052 053 // Parse general information of the view Configuration 054 _fillViewGeneralInformation(viewConfiguration, view, view, model); 055 056 // Parse the view items of the view Configuration 057 for (Configuration itemConfiguration : viewConfiguration.configuration().getChildren()) 058 { 059 _parseViewChild(new ConfigurationAndPluginName(itemConfiguration, viewConfiguration.pluginName(), viewConfiguration.defaultI18nCatalog()), view, model, false, context); 060 } 061 062 return view; 063 } 064 065 /** 066 * Parses the name of the view 067 * @param viewConfiguration the view's configuration 068 * @return the view's name 069 * @throws ConfigurationException if an error occurs while parsing the view's name 070 */ 071 protected String _parseViewName(ConfigurationAndPluginName viewConfiguration) throws ConfigurationException 072 { 073 return viewConfiguration.configuration().getAttribute("name"); 074 } 075 076 public View overrideView(ConfigurationAndPluginName viewConfiguration, View existingView, ViewParserContext context) throws ConfigurationException 077 { 078 View view = new View(); 079 view.setName(existingView.getName()); 080 081 // Get the model 082 Collection<? extends Model> model = _getModel(); 083 084 // Parse general information of the view Configuration 085 _fillViewGeneralInformation(viewConfiguration, view, existingView, model); 086 087 // Parse the view items of the view Configuration 088 view.addViewItems(existingView.getViewItems()); 089 for (Configuration itemConfiguration : viewConfiguration.configuration().getChildren()) 090 { 091 _parseViewChild(new ConfigurationAndPluginName(itemConfiguration, viewConfiguration.pluginName(), viewConfiguration.defaultI18nCatalog()), view, model, true, context); 092 } 093 094 return view; 095 } 096 097 /** 098 * Retrieves the model corresponding to the view to parse 099 * @return the model 100 */ 101 protected abstract Collection<? extends Model> _getModel(); 102 103 /** 104 * Fill the general information of the given view (label, description, ...) 105 * @param viewConfiguration the configuration of the view to fill 106 * @param view the view to fill 107 * @param existingView the existing view, that may already contain general information 108 * @param model The model of the view 109 * @throws ConfigurationException if the configuration is not valid. 110 */ 111 protected void _fillViewGeneralInformation(ConfigurationAndPluginName viewConfiguration, View view, View existingView, Collection<? extends Model> model) throws ConfigurationException 112 { 113 // Internal 114 view.setInternal(viewConfiguration.configuration().getAttributeAsBoolean("internal", existingView.isInternal())); 115 116 // Label 117 I18nizableText label = existingView.getLabel() == null 118 ? ItemParserHelper.parseI18nizableText(viewConfiguration, "label", existingView.getName()) 119 : ItemParserHelper.parseI18nizableText(viewConfiguration, "label", existingView.getLabel()); // Case of override, use the existing view as default value 120 view.setLabel(label); 121 122 // Description 123 I18nizableText description = existingView.getDescription() == null 124 ? ItemParserHelper.parseI18nizableText(viewConfiguration, "description") // default value is an empty string 125 : ItemParserHelper.parseI18nizableText(viewConfiguration, "description", existingView.getDescription()); // Case of override, use the existing view as default value 126 view.setDescription(description); 127 } 128 129 /** 130 * Parses the item with the given configuration and add the item to the given view 131 * @param itemConfiguration the configuration of the item to parse 132 * @param view the view 133 * @param model The model containing the parsed item 134 * @param override <code>true</code> if the configuration is an override, <code>false</code> otherwise 135 * @param context the context of the view parsing 136 * @throws ConfigurationException if the configuration is not valid. 137 */ 138 protected void _parseViewChild(ConfigurationAndPluginName itemConfiguration, View view, Collection<? extends Model> model, boolean override, ViewParserContext context) throws ConfigurationException 139 { 140 if (_isAddingItemConfiguration(itemConfiguration.configuration())) 141 { 142 _parseModelViewItem(itemConfiguration, view, model, view, override, context); 143 } 144 else if (_isRemovingItemConfiguration(itemConfiguration.configuration())) 145 { 146 _removeViewItemFromView(view, itemConfiguration.configuration()); 147 } 148 else if (_isAddingGroupConfiguration(itemConfiguration.configuration())) 149 { 150 _parseSimpleViewItemGroup(itemConfiguration, ViewItemGroup.TAB_ROLE, view, model, view, override, context); 151 } 152 } 153 154 /** 155 * Checks if the given item configuration is an adding item configuration 156 * @param itemConfiguration the item configuration 157 * @return <code>true</code> if the given item configuration is an adding item configuration, <code>false</code> otherwise 158 */ 159 protected boolean _isAddingItemConfiguration(Configuration itemConfiguration) 160 { 161 String itemConfigurationName = itemConfiguration.getName(); 162 return ADD_ITEM_TAG_NAME.equals(itemConfigurationName); 163 } 164 165 /** 166 * Checks if the given item configuration is a removing item configuration 167 * @param itemConfiguration the item configuration 168 * @return <code>true</code> if the given item configuration is a removing item configuration, <code>false</code> otherwise 169 */ 170 protected boolean _isRemovingItemConfiguration(Configuration itemConfiguration) 171 { 172 String itemConfigurationName = itemConfiguration.getName(); 173 return REMOVE_ITEM_TAG_NAME.equals(itemConfigurationName); 174 } 175 176 /** 177 * Checks if the given item configuration is an adding group configuration 178 * @param itemConfiguration the item configuration 179 * @return <code>true</code> if the given item configuration is an adding group configuration, <code>false</code> otherwise 180 */ 181 protected boolean _isAddingGroupConfiguration(Configuration itemConfiguration) 182 { 183 String itemConfigurationName = itemConfiguration.getName(); 184 return ADD_GROUP_TAG_NAME.equals(itemConfigurationName); 185 } 186 187 /** 188 * Parses a model view item and add it to its parent view item accessor 189 * @param itemConfiguration configuration of the model view item 190 * @param parentViewItemAccessor the parent view item accessor of the model view item to parse 191 * @param parentModelItemAccessors the parents model item accessors of the model item corresponding to the model view item to parse 192 * @param referenceView view that references the item 193 * @param override <code>true</code> if the configuration is an override, <code>false</code> otherwise 194 * @param context the context of the view parsing 195 * @throws ConfigurationException if the configuration is not valid. 196 */ 197 protected void _parseModelViewItem(ConfigurationAndPluginName itemConfiguration, ViewItemAccessor parentViewItemAccessor, Collection<? extends ModelItemAccessor> parentModelItemAccessors, View referenceView, boolean override, ViewParserContext context) throws ConfigurationException 198 { 199 String modelItemReference = _getModelItemReference(itemConfiguration.configuration()); 200 201 Matcher viewReferenceMatcher = __VIEW_REFERENCE_PATTERN.matcher(modelItemReference); 202 boolean hasViewReference = viewReferenceMatcher.matches(); 203 204 String modelItemPath = hasViewReference ? modelItemReference.replaceAll(__VIEW_REFERENCE_REGEX, StringUtils.EMPTY) : modelItemReference; // Remove potential view reference 205 206 int lastIndexOfItemPathSeparator = !hasViewReference 207 // Resolve model item reference and initialize parents if the reference is a path 208 ? modelItemPath.lastIndexOf(ModelItem.ITEM_PATH_SEPARATOR) 209 // For view references, the model item is created as a parent view item accessor 210 : modelItemPath.length(); 211 212 213 ViewItemAccessor finalParentViewItemAccessor = parentViewItemAccessor; 214 Collection<? extends ModelItemAccessor> finalParentModelItemAccessors = parentModelItemAccessors; 215 216 String modelItemName = modelItemPath; 217 218 if (lastIndexOfItemPathSeparator > -1) 219 { 220 // Get or create the view item accessors associated to the model item path prefix 221 String parentRelativePath = modelItemPath.substring(0, lastIndexOfItemPathSeparator); 222 finalParentViewItemAccessor = _createViewItemAccessor(itemConfiguration.configuration(), parentRelativePath, parentViewItemAccessor, parentModelItemAccessors, referenceView, override); 223 finalParentModelItemAccessors = List.of((ModelItemAccessor) ((ModelViewItem) finalParentViewItemAccessor).getDefinition()); 224 225 modelItemName = !hasViewReference ? modelItemPath.substring(lastIndexOfItemPathSeparator + ModelItem.ITEM_PATH_SEPARATOR.length()) : StringUtils.EMPTY; 226 } 227 228 if (hasViewReference) 229 { 230 String viewName = viewReferenceMatcher.group(1); 231 _parseViewReference(viewName, finalParentViewItemAccessor, finalParentModelItemAccessors.iterator().next()); 232 } 233 else if (ALL_ITEMS_REFERENCE.equals(modelItemName)) 234 { 235 parseAllModelViewItems(itemConfiguration, finalParentViewItemAccessor, finalParentModelItemAccessors, referenceView, override, context); 236 } 237 else 238 { 239 // Get the model item 240 ModelItem modelItem = _getModelItem(itemConfiguration, modelItemName, finalParentModelItemAccessors); 241 242 // Create the view item corresponding to the model item 243 ModelViewItem viewItem = createModelViewItem(itemConfiguration, modelItem, referenceView, override, context); 244 245 // Add the view item to its parent 246 _addItemToViewItemAccessor(finalParentViewItemAccessor, viewItem, itemConfiguration.configuration(), referenceView, override); 247 } 248 } 249 250 /** 251 * Parses the view reference to add items of the view to the given item accessor 252 * @param viewName the name of the referenced view 253 * @param viewItemAccessor the view item accessor referencing the view 254 * @param definition the definition of the model item containing the referenced view 255 * @throws ConfigurationException if the configuration is not valid 256 */ 257 protected abstract void _parseViewReference(String viewName, ViewItemAccessor viewItemAccessor, ModelItemAccessor definition) throws ConfigurationException; 258 259 /** 260 * Parses all the model view items of the given parents 261 * @param itemConfiguration the item configuration 262 * @param parentViewItemAccessor the parent view item accessor 263 * @param parentModelItemAccessors the parent model item accorssors 264 * @param referenceView view that references the item 265 * @param override <code>true</code> if the configuration is an override, <code>false</code> otherwise 266 * @param context the context of the view parsing 267 * @throws ConfigurationException if the configuration is not valid. 268 */ 269 protected void parseAllModelViewItems(ConfigurationAndPluginName itemConfiguration, ViewItemAccessor parentViewItemAccessor, Collection<? extends ModelItemAccessor> parentModelItemAccessors, View referenceView, boolean override, ViewParserContext context) throws ConfigurationException 270 { 271 // For all accessors' items 272 for (ModelItem modelItem : ModelHelper.getModelItems(parentModelItemAccessors)) 273 { 274 // Create the view item corresponding to the model item 275 ModelViewItem viewItem = createModelViewItemForAllItemsReference(itemConfiguration, modelItem, referenceView, override, context); 276 277 // Add the view item to its parent 278 _addItemToViewItemAccessor(parentViewItemAccessor, viewItem, itemConfiguration.configuration(), referenceView, override); 279 280 // Recursively add all item's children 281 if (modelItem instanceof ModelItemContainer modelItemContainer && viewItem instanceof ViewItemContainer viewItemContainer) 282 { 283 parseAllModelViewItems(itemConfiguration, viewItemContainer, List.of(modelItemContainer), referenceView, override, context); 284 } 285 } 286 } 287 288 /** 289 * Creates the model view item corresponding to the given configuration, in cas of all items references 290 * @param itemConfiguration configuration of the model view item 291 * @param modelItem the model item 292 * @param referenceView view that references the item 293 * @param override <code>true</code> if the configuration is an override, <code>false</code> otherwise 294 * @param context the context of the view parsing 295 * @return the created model view item 296 * @throws ConfigurationException if the configuration is not valid. 297 */ 298 protected ModelViewItem createModelViewItemForAllItemsReference(ConfigurationAndPluginName itemConfiguration, ModelItem modelItem, View referenceView, boolean override, ViewParserContext context) throws ConfigurationException 299 { 300 return createModelViewItem(itemConfiguration, modelItem, referenceView, override, context); 301 } 302 303 /** 304 * Creates the model view item corresponding to the given configuration 305 * @param itemConfiguration configuration of the model view item 306 * @param modelItem the model item 307 * @param referenceView view that references the item 308 * @param override <code>true</code> if the configuration is an override, <code>false</code> otherwise 309 * @param context the context of the view parsing 310 * @return the created model view item 311 * @throws ConfigurationException if the configuration is not valid. 312 */ 313 protected ModelViewItem createModelViewItem(ConfigurationAndPluginName itemConfiguration, ModelItem modelItem, View referenceView, boolean override, ViewParserContext context) throws ConfigurationException 314 { 315 ModelViewItem viewItem; 316 if (modelItem instanceof ModelItemGroup modelItemGroup) 317 { 318 viewItem = _createLeafModelViewItemInstance(modelItemGroup); 319 for (Configuration childConfiguration : itemConfiguration.configuration().getChildren()) 320 { 321 _parseViewItemAccessorChild(new ConfigurationAndPluginName(childConfiguration, itemConfiguration.pluginName(), itemConfiguration.defaultI18nCatalog()), (ViewItemAccessor) viewItem, modelItemGroup, referenceView, override, context); 322 } 323 } 324 else 325 { 326 viewItem = _parseViewElement(itemConfiguration, (ElementDefinition) modelItem, referenceView, override, context); 327 } 328 329 // Parse label and description 330 if (itemConfiguration.configuration().getChild("label", false) != null) 331 { 332 viewItem.setLabel(ItemParserHelper.parseI18nizableText(itemConfiguration, "label")); 333 } 334 if (itemConfiguration.configuration().getChild("description", false) != null) 335 { 336 viewItem.setDescription(ItemParserHelper.parseI18nizableText(itemConfiguration, "description")); 337 } 338 339 // Parse disabled item rendering 340 Optional.of(itemConfiguration) 341 .map(ConfigurationAndPluginName::configuration) 342 .map(config -> config.getAttribute("disabled", DisabledItemRendering.DEFAULT.name())) 343 .map(String::toUpperCase) 344 .map(DisabledItemRendering::valueOf) 345 .ifPresent(viewItem::setDisabledItemRendering); 346 347 return viewItem; 348 } 349 350 /** 351 * Creates view items corresponding to the given path. 352 * @param itemConfiguration the configuration containing the model item reference 353 * @param modelItemAccessorPath The path of the model item accessor to retrieve 354 * @param viewItemAccessor the view item accessor relative to the given path 355 * @param modelItemAccessors the model item accessor relative to the given path 356 * @param referenceView the reference view for includes 357 * @param override <code>true</code> if the configuration is an override, <code>false</code> otherwise 358 * @return the found or created view item accessor 359 * @throws ConfigurationException if the given path does not correspond to a model item accessor 360 */ 361 protected ViewItemAccessor _createViewItemAccessor(Configuration itemConfiguration, String modelItemAccessorPath, ViewItemAccessor viewItemAccessor, Collection<? extends ModelItemAccessor> modelItemAccessors, View referenceView, boolean override) throws ConfigurationException 362 { 363 int firstIndexOfItemPathSeparator = modelItemAccessorPath.indexOf(ModelItem.ITEM_PATH_SEPARATOR); 364 String firstPathSegment = firstIndexOfItemPathSeparator > -1 ? modelItemAccessorPath.substring(0, modelItemAccessorPath.indexOf(ModelItem.ITEM_PATH_SEPARATOR)) : modelItemAccessorPath; 365 366 ModelItem modelItem = ModelHelper.getModelItem(firstPathSegment, modelItemAccessors); 367 368 if (modelItem instanceof ModelItemAccessor) 369 { 370 // Create the view item and add it to the current view item accessor 371 ModelViewItem viewItem = _createModelViewItemInstance(modelItem); 372 373 // Add the view item to its parent 374 _addItemToViewItemAccessor(viewItemAccessor, viewItem, itemConfiguration, referenceView, override); 375 376 if (firstIndexOfItemPathSeparator > -1) 377 { 378 // Only the first segment of the path has been processed, now recursively process the next ones 379 String subPath = modelItemAccessorPath.substring(firstIndexOfItemPathSeparator + 1); 380 return _createViewItemAccessor(itemConfiguration, subPath, (ViewItemAccessor) viewItem, List.of((ModelItemAccessor) modelItem), referenceView, override); 381 } 382 else 383 { 384 return (ViewItemAccessor) viewItem; 385 } 386 } 387 else 388 { 389 throw new ConfigurationException("Unable to get or create a view item accessor, the given path '" + modelItemAccessorPath + "' refers to a model item that is not an accessor"); 390 } 391 } 392 393 /** 394 * Retrieves the model item with the given name 395 * @param itemConfiguration configuration of the model view item 396 * @param modelItemName the model item name 397 * @param parents the accessors containing the model item 398 * @return the model item 399 * @throws ConfigurationException if the configuration is not valid. 400 */ 401 protected ModelItem _getModelItem(ConfigurationAndPluginName itemConfiguration, String modelItemName, Collection<? extends ModelItemAccessor> parents) throws ConfigurationException 402 { 403 try 404 { 405 return ModelHelper.getModelItem(modelItemName, parents); 406 } 407 catch (IllegalArgumentException | UndefinedItemPathException e) 408 { 409 throw new ConfigurationException("The item '" + modelItemName + "' is not defined in model.", itemConfiguration.configuration(), e); 410 } 411 } 412 413 /** 414 * Retrieves the model item reference from the given item configuration 415 * @param itemConfiguration the item configuration 416 * @return the model item reference 417 * @throws ConfigurationException if an error occurs while parsing the model item reference 418 */ 419 protected String _getModelItemReference(Configuration itemConfiguration) throws ConfigurationException 420 { 421 return itemConfiguration.getAttribute(ITEM_REFERENCE_ATTRIBUTE_NAME); 422 } 423 424 /** 425 * Retrieves the child configuration to add items to the current accessor 426 * @param accessorConfiguration the accessor configuration 427 * @return the child configuration to add items to the current accessor 428 */ 429 protected Configuration _getAddItemChildConfiguration(Configuration accessorConfiguration) 430 { 431 return accessorConfiguration.getChild(ADD_ITEM_TAG_NAME, false); 432 } 433 434 /** 435 * Parses the view element 436 * @param itemConfiguration configuration of the view item 437 * @param definition definition of the element 438 * @param referenceView view that references the item 439 * @param override <code>true</code> if the configuration is an override, <code>false</code> otherwise 440 * @param context the context of the view parsing 441 * @return the view item 442 * @throws ConfigurationException if the configuration is not valid 443 */ 444 protected ViewElement _parseViewElement(ConfigurationAndPluginName itemConfiguration, ElementDefinition definition, View referenceView, boolean override, ViewParserContext context) throws ConfigurationException 445 { 446 return (ViewElement) _createLeafModelViewItemInstance(definition); 447 } 448 449 /** 450 * Creates an instance of {@link ModelViewItem} due to the given {@link ModelItem} 451 * @param modelItem the model item corresponding to the view item to create 452 * @return the created view item 453 */ 454 @SuppressWarnings("unchecked") 455 protected ModelViewItem _createModelViewItemInstance(ModelItem modelItem) 456 { 457 ModelViewItem modelViewItem = ViewHelper.createModelViewItemInstance(modelItem); 458 modelViewItem.setDefinition(modelItem); 459 460 return modelViewItem; 461 } 462 463 /** 464 * Creates an instance of {@link ModelViewItem} due to the given {@link ModelItem}. The view item is a leaf of the view 465 * @param modelItem the model item corresponding to the view item to create 466 * @return the created view item 467 */ 468 protected ModelViewItem _createLeafModelViewItemInstance(ModelItem modelItem) 469 { 470 return _createModelViewItemInstance(modelItem); 471 } 472 473 /** 474 * Parses the item with the given configuration and add the item to the given view item accessor 475 * @param itemConfiguration the configuration of the item to parse 476 * @param viewItemAccessor the {@link ViewItemAccessor} that will access to the parsed items 477 * @param modelItemAccessor the {@link ModelItemAccessor} corresponding to the given view item accessor 478 * @param referenceView The view that references the item 479 * @param override <code>true</code> if the configuration is an override, <code>false</code> otherwise 480 * @param context the context of the view parsing 481 * @throws ConfigurationException if the configuration is not valid 482 */ 483 protected void _parseViewItemAccessorChild(ConfigurationAndPluginName itemConfiguration, ViewItemAccessor viewItemAccessor, ModelItemAccessor modelItemAccessor, View referenceView, boolean override, ViewParserContext context) throws ConfigurationException 484 { 485 if (_isAddingItemConfiguration(itemConfiguration.configuration())) 486 { 487 _parseModelViewItem(itemConfiguration, viewItemAccessor, List.of(modelItemAccessor), referenceView, override, context); 488 } 489 else if (_isAddingGroupConfiguration(itemConfiguration.configuration())) 490 { 491 _parseSimpleViewItemGroup(itemConfiguration, ViewItemGroup.FIELDSET_ROLE, viewItemAccessor, List.of(modelItemAccessor), referenceView, override, context); 492 } 493 } 494 495 /** 496 * Parses a simple view item group and add it to its parent 497 * @param itemConfiguration configuration of the simple view item group 498 * @param role the role of the view group 499 * @param parent the parent view item accessor 500 * @param modelItemAccessors the current parent model item accessors 501 * @param referenceView the reference view for includes 502 * @param override <code>true</code> if the configuration is an override, <code>false</code> otherwise 503 * @param context the context of the view parsing 504 * @throws ConfigurationException if the configuration is not valid. 505 */ 506 protected void _parseSimpleViewItemGroup(ConfigurationAndPluginName itemConfiguration, String role, ViewItemAccessor parent, Collection<? extends ModelItemAccessor> modelItemAccessors, View referenceView, boolean override, ViewParserContext context) throws ConfigurationException 507 { 508 SimpleViewItemGroup group = new SimpleViewItemGroup(); 509 group.setRole(itemConfiguration.configuration().getAttribute("role", role)); 510 group.setName(itemConfiguration.configuration().getAttribute("name", null)); 511 512 group.setLabel(ItemParserHelper.parseI18nizableText(itemConfiguration, "label")); 513 group.setDescription(ItemParserHelper.parseI18nizableText(itemConfiguration, "description")); 514 515 for (Configuration childConfiguration : itemConfiguration.configuration().getChildren()) 516 { 517 _parseSimpleViewItemGroupChild(new ConfigurationAndPluginName(childConfiguration, itemConfiguration.pluginName(), itemConfiguration.defaultI18nCatalog()), group, modelItemAccessors, referenceView, override, context); 518 } 519 520 // Add the group to its parent 521 _addItemToViewItemAccessor(parent, group, itemConfiguration.configuration(), referenceView, override); 522 } 523 524 /** 525 * Parses the item with the given configuration and add the item to the given group 526 * @param itemConfiguration configuration of the group's child 527 * @param group the simple view item group 528 * @param modelItemAccessors the current parent model item accessors 529 * @param referenceView the reference view for includes 530 * @param override <code>true</code> if the configuration is an override, <code>false</code> otherwise 531 * @param context the context of the view parsing 532 * @throws ConfigurationException if the configuration is not valid. 533 */ 534 protected void _parseSimpleViewItemGroupChild(ConfigurationAndPluginName itemConfiguration, SimpleViewItemGroup group, Collection<? extends ModelItemAccessor> modelItemAccessors, View referenceView, boolean override, ViewParserContext context) throws ConfigurationException 535 { 536 if (_isAddingItemConfiguration(itemConfiguration.configuration())) 537 { 538 _parseModelViewItem(itemConfiguration, group, modelItemAccessors, referenceView, override, context); 539 } 540 else if (_isAddingGroupConfiguration(itemConfiguration.configuration())) 541 { 542 _parseSimpleViewItemGroup(itemConfiguration, ViewItemGroup.FIELDSET_ROLE, group, modelItemAccessors, referenceView, override, context); 543 } 544 } 545 546 /** 547 * Add an item to a view or to an overridden view 548 * @param viewItemAccessor The view 549 * @param viewItem The view item to add 550 * @param itemConfiguration The item's configurations 551 * @param referenceView the reference view for includes 552 * @param override <code>true</code> if the view is an override, <code>false</code> otherwise 553 * @throws ConfigurationException If an error occurs 554 */ 555 protected void _addItemToViewItemAccessor(ViewItemAccessor viewItemAccessor, ViewItem viewItem, Configuration itemConfiguration, View referenceView, boolean override) throws ConfigurationException 556 { 557 // Check already existing view items 558 if (getLogger().isWarnEnabled() && viewItem instanceof ModelViewItem modelViewItem && (viewItemAccessor.hasModelViewItem(modelViewItem) || referenceView.hasModelViewItem(modelViewItem))) 559 { 560 String itemPath = modelViewItem.getDefinition().getPath(); 561 getLogger().warn("The item '" + itemPath + "' is already referenced by the view '" + referenceView.getName() + "'."); 562 } 563 564 if (viewItemAccessor instanceof View view && override) 565 { 566 _addItemToOverriddenView(view, viewItem, itemConfiguration); 567 } 568 else 569 { 570 viewItemAccessor.addViewItem(viewItem); 571 } 572 } 573 574 /** 575 * Add the viewItem to the view or in a location inside the view (in a group or before/after another view item) 576 * @param view The view in which to insert the new Item 577 * @param viewItem The viewItem to insert 578 * @param itemConfiguration The configuration of the viewItem 579 * @throws ConfigurationException If an error occurs 580 */ 581 protected void _addItemToOverriddenView(View view, ViewItem viewItem, Configuration itemConfiguration) throws ConfigurationException 582 { 583 String group = itemConfiguration.getAttribute("group", null); 584 ViewItemAccessor viewItemAccessor = view; 585 586 if (group != null) 587 { 588 try 589 { 590 viewItemAccessor = ViewHelper.getSimpleViewItemGroup(view, group); 591 } 592 catch (IllegalArgumentException e) 593 { 594 throw new ConfigurationException("The path to the requested group where to add the view item " + viewItem.getName() + " in the initial view " + view.getName() + " is empty.", itemConfiguration, e); 595 } 596 catch (UndefinedItemPathException e) 597 { 598 getLogger().warn("The group requested " + group + " does not exist in the initial view " + view.getName() + ". The item '" + viewItem.getName() + "' will be inserted at the end of the view", e); 599 } 600 } 601 602 _insertItemInViewItemAccessor(viewItemAccessor, viewItem, itemConfiguration); 603 } 604 605 /** 606 * Insert an item in the given {@link ViewItemAccessor} 607 * @param viewItemAccessor The view item accessor in which to insert the new Item 608 * @param viewItem The viewItem to insert 609 * @param itemConfiguration The configuration of the viewItem 610 * @throws ConfigurationException If an error occurs 611 */ 612 protected void _insertItemInViewItemAccessor(ViewItemAccessor viewItemAccessor, ViewItem viewItem, Configuration itemConfiguration) throws ConfigurationException 613 { 614 String before = itemConfiguration.getAttribute("order-before", null); 615 String after = itemConfiguration.getAttribute("order-after", null); 616 617 // An attribute cannot be before an attribute and after at the same time 618 if (after != null && before != null) 619 { 620 throw new ConfigurationException("The item " + viewItem.getName() + " cannot be added both after and before attributes", itemConfiguration); 621 } 622 623 if (after != null || before != null) 624 { 625 InsertMode insertMode = after != null ? InsertMode.AFTER : InsertMode.BEFORE; 626 String insertAfterOrBefore = after != null ? after : before; 627 628 try 629 { 630 ViewHelper.insertItemAfterOrBefore(viewItemAccessor, viewItem, insertAfterOrBefore, insertMode); 631 } 632 catch (IllegalArgumentException e) 633 { 634 throw new ConfigurationException("Unable to insert view item " + viewItem.getName() + " " + insertMode + " the specified view item. The name is empty or is a path.", itemConfiguration, e); 635 } 636 catch (UndefinedItemPathException e) 637 { 638 if (getLogger().isWarnEnabled()) 639 { 640 String viewItemAccessorName = viewItemAccessor instanceof View view 641 ? view.getName() 642 : viewItemAccessor instanceof ViewItem vI 643 ? vI.getName() 644 : viewItemAccessor.toString(); 645 getLogger().warn("Unable to insert view item " + viewItem.getName() + " " + insertMode + " the view item named " + insertAfterOrBefore + ". No view item has been found with this name. This item will be inserted at the end of the view item accessor '" + viewItemAccessorName + "'.", e); 646 } 647 648 // Add the item at the end of the view item accessor 649 viewItemAccessor.addViewItem(viewItem); 650 } 651 } 652 else 653 { 654 viewItemAccessor.addViewItem(viewItem); 655 } 656 } 657 658 /** 659 * Removes a view item from the view 660 * @param view The view from which we want to remove an item 661 * @param itemConfiguration The configuration of the view item to remove 662 * @throws ConfigurationException If there is an error whil parsing the item configuration 663 */ 664 protected void _removeViewItemFromView(View view, Configuration itemConfiguration) throws ConfigurationException 665 { 666 String viewItemPath = itemConfiguration.getAttribute(ITEM_REFERENCE_ATTRIBUTE_NAME); 667 668 try 669 { 670 ViewItem viewItem = ViewHelper.getViewItem(view, viewItemPath); 671 ViewItemAccessor parent = viewItem.getParent(); 672 parent.removeViewItem(viewItem); 673 } 674 catch (Exception e) 675 { 676 // Just log the warning, do not throw exceptions 677 getLogger().warn("[View Item removal] Unable to remove " + viewItemPath + " from " + view.getName(), e); 678 } 679 } 680}