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