001/*
002 *  Copyright 2016 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.core.ui.ribbonconfiguration;
017
018import java.util.ArrayList;
019import java.util.HashMap;
020import java.util.List;
021import java.util.Map;
022
023import org.slf4j.Logger;
024
025/**
026 * Helper for the ribbon, for injecting elements inside another elements' container
027 * @param <T> The type of element to inject
028 */
029public class RibbonElementsInjectionHelper<T>
030{
031    /** The list of elements */
032    protected List<T> _elements;
033
034    /** Logger */
035    protected Logger _logger;
036
037    private  Map<Integer, ObjectOrderMapping> _mapping = new HashMap<>();
038
039    /**
040     * Map a list of elements by they order to allows for elements injection 
041     * @param elements The list of elements
042     * @param logger The logger
043     */
044    public RibbonElementsInjectionHelper(List<T> elements, Logger logger)
045    {
046        _logger = logger;
047        _elements = elements;
048        
049        if (elements.size() > 0)
050        {
051            for (T element : elements)
052            {
053                _mapping.put(elements.indexOf(element) + 1, new ObjectOrderMapping(element)); 
054            }
055        }
056        else
057        {
058            _mapping.put(1, new ObjectOrderMapping(null)); 
059        }
060    }
061    
062    /**
063     * Inject an element into the list of elements
064     * @param inject The element to inject
065     * @param order The order property. 
066     */
067    public void injectElements(T inject, String order)
068    {
069        int indexOfDot = order.indexOf(".");
070        int primaryOrder =  Integer.valueOf(indexOfDot > 0 ? order.substring(0, indexOfDot) : order);
071        Integer secondaryOrder =  indexOfDot > 0 ? Integer.valueOf(order.substring(indexOfDot + 1)) : null;
072        if (primaryOrder > 0)
073        {
074            if (primaryOrder > _mapping.size() || !_mapping.containsKey(primaryOrder))
075            {
076                // out of bound
077                if (_logger.isDebugEnabled())
078                {
079                    _logger.debug("TabOverrideHelper : injecting element '" + inject.toString() + "' with order '" + primaryOrder + "', but that order is out of bound. The element is injected at the end, after element '" + _mapping.get(_mapping.size()).getInitialObject() + "'");
080                }
081
082                _mapping.get(_mapping.size()).injectObjectAfter(inject, null);
083            }
084            else
085            {
086                _mapping.get(primaryOrder).injectObjectBefore(inject, secondaryOrder);
087                
088                if (_logger.isDebugEnabled())
089                {
090                    _logger.debug("TabOverrideHelper : injecting element '" + inject.toString() + "' with order '" + primaryOrder + "' before element '" + _mapping.get(primaryOrder).getInitialObject() + "'");
091                }
092            }
093        }
094        else
095        {
096            int relativeOrder = _mapping.size() + primaryOrder;
097            if (relativeOrder < 0 || !_mapping.containsKey(relativeOrder))
098            {
099                // out of bound
100                if (_logger.isDebugEnabled())
101                {
102                    _logger.debug("TabOverrideHelper : injecting element '" + inject.toString() + "' with order '" + primaryOrder + "', but that order is out of bound. The element is injected at the start, before element '" + _mapping.get(1).getInitialObject() + "'");
103                }
104
105                _mapping.get(1).injectObjectBefore(inject, 0);
106            }
107            else
108            {
109                if (_logger.isDebugEnabled())
110                {
111                    _logger.debug("TabOverrideHelper : injecting element '" + inject.toString() + "' with order '" + primaryOrder + "' after element '" + _mapping.get(relativeOrder).getInitialObject() + "'");
112                }
113                
114                _mapping.get(relativeOrder).injectObjectAfter(inject, secondaryOrder);
115            }
116        }
117    }
118
119    private class ObjectOrderMapping
120    {   
121        private T _initialObject;
122        private int _initialObjectIndex;
123        private List<T> _objectsBefore = new ArrayList<>();
124        private Map<T, Integer> _objectsBeforeOrder = new HashMap<>();
125        private List<T> _objectsAfter = new ArrayList<>();
126        private Map<T, Integer> _objectsAfterOrder = new HashMap<>();
127
128        public ObjectOrderMapping(T initialObject)
129        {
130            _initialObject = initialObject;
131            _initialObjectIndex = _initialObject != null ? _elements.indexOf(_initialObject) : 0;
132        }
133        
134        public void injectObjectBefore(T object, Integer order)
135        {
136            if (_objectsBefore.size() == 0)
137            {
138                _objectsBefore.add(object);
139                if (order != null)
140                {
141                    _objectsBeforeOrder.put(object, order);
142                }
143                
144                _elements.add(_initialObject != null ? _elements.indexOf(_initialObject) : 0, object);
145            }
146            else
147            {
148                _inject(object, order, _objectsBefore, _objectsBeforeOrder);
149            }
150            _initialObjectIndex++;
151        }
152        
153        public void injectObjectAfter(T object, Integer order)
154        {
155            if (_objectsAfter.size() == 0)
156            {
157                _objectsAfter.add(object);
158                if (order != null)
159                {
160                    _objectsAfterOrder.put(object, order);
161                }
162
163                _elements.add(_initialObject != null ? _elements.indexOf(_initialObject) + 1 : _elements.size(), object);
164            }
165            else
166            {
167                _inject(object, order, _objectsAfter, _objectsAfterOrder);
168            }
169        }
170        
171        private void _inject(T object, Integer order, List<T> objects, Map<T, Integer> objectsOrder)
172        {
173            if (order == null)
174            {
175                T lastObject = objects.get(objects.size() - 1);
176                _elements.add(_elements.indexOf(lastObject) + 1, object);
177                objects.add(object);
178                
179                return;
180            }
181            
182            T previous = null;
183            for (int i = 0; i < objects.size() && objectsOrder.containsKey(objects.get(i)) &&  objectsOrder.get(objects.get(i)) <= order; i++)
184            {
185                previous = objects.get(i);
186            }
187            
188            objectsOrder.put(object, order);
189            if (previous == null)
190            {
191                T firstObject = objects.get(0);
192                _elements.add(_elements.indexOf(firstObject), object);
193                objects.add(0, object);
194            }
195            else
196            {
197                _elements.add(_elements.indexOf(previous) + 1, object);
198                objects.add(objects.indexOf(previous) + 1, object);
199            }
200        }
201        
202        public T getInitialObject()
203        {
204            return _initialObject;
205        }
206    }
207}