001/*
002 *  Copyright 2020 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.plugins.repository.data.holder.values;
017
018import java.util.List;
019import java.util.Map;
020import java.util.Optional;
021import java.util.Set;
022import java.util.function.Function;
023import java.util.stream.Collectors;
024import java.util.stream.IntStream;
025
026/**
027 * Wrapper for a synchronizable repeater.<br>
028 * Contains the repeater values and a mapping between stored entry positions and new ones.
029 */
030public final class SynchronizableRepeater
031{
032    private List<Map<String, Object>> _entries;
033    private Map<Integer, Integer> _positionsMapping;
034    private List<Integer> _replacePositions;
035    private Set<Integer> _removedEntries;
036    private Mode _mode;
037    
038    /**
039     * The entries write mode.
040     */
041    public static enum Mode
042    {
043        /** The entries will replace all existing values */
044        REPLACE_ALL,
045        /** The entries will replace specific entries */
046        REPLACE,
047        /** The entries will be appended after existing entries */
048        APPEND
049    }
050    
051    private SynchronizableRepeater(List<Map<String, Object>> entries, Mode mode)
052    {
053        _entries = entries;
054        _mode = mode;
055    }
056    
057    private SynchronizableRepeater(List<Map<String, Object>> entries, Map<Integer, Integer> positionsMapping, List<Integer> replacePositions, Set<Integer> removedEntries, Mode mode)
058    {
059        _entries = entries;
060        _positionsMapping = positionsMapping;
061        _replacePositions = replacePositions;
062        _removedEntries = removedEntries;
063        _mode = mode;
064    }
065    
066    /**
067     * Replace all values in a repeater, optionally moving or removing entries. Entries not present in the mapping will be removed.
068     * @param entries the new repeater values.
069     * @param positionsMapping a mapping from stored positions to new ones. No mapping for an existing entry means removal. A null mapping means the identity mapping.
070     * @return a {@link SynchronizableRepeater} in replace mode.
071     */
072    public static SynchronizableRepeater replaceAll(List<Map<String, Object>> entries, Map<Integer, Integer> positionsMapping)
073    {
074        SynchronizableRepeater repeater = new SynchronizableRepeater(entries, Mode.REPLACE_ALL);
075        repeater._positionsMapping = positionsMapping != null ? positionsMapping : IntStream.rangeClosed(1, entries.size()).boxed().collect(Collectors.toMap(Function.identity(), Function.identity()));
076        
077        return repeater;
078    }
079    
080    /**
081     * Replace values in a repeater.
082     * @param entries the new repeater values.
083     * @param positions the positions of the provided entries in the repeater. A null List means replacing the first entries.
084     * @return a {@link SynchronizableRepeater} in replace mode.
085     */
086    public static SynchronizableRepeater replace(List<Map<String, Object>> entries, List<Integer> positions)
087    {
088        assert positions == null || positions.size() == entries.size();
089        SynchronizableRepeater repeater = new SynchronizableRepeater(entries, Mode.REPLACE);
090        repeater._replacePositions = positions != null ? positions : IntStream.rangeClosed(1, entries.size()).boxed().collect(Collectors.toList());
091        
092        return repeater;
093    }
094    
095    /**
096     * Append and/or remove entries in a repeater.
097     * @param newEntries the entries to be appended to the repeater.
098     * @param removedEntries the entries to be removed from the repeater.
099     * @return a {@link SynchronizableRepeater} in append mode.
100     */
101    public static SynchronizableRepeater appendOrRemove(List<Map<String, Object>> newEntries, Set<Integer> removedEntries)
102    {
103        SynchronizableRepeater repeater = new SynchronizableRepeater(newEntries, Mode.APPEND);
104        repeater._removedEntries = removedEntries;
105        
106        return repeater;
107    }
108    
109    /**
110     * Copy the provided repeater, optionally replacing entries with given ones.
111     * @param repeater the SynchronizableRepeater to copy
112     * @param newEntries the new entries values
113     * @return a new SynchronizableRepeater
114     */
115    public static SynchronizableRepeater copy(SynchronizableRepeater repeater, List<Map<String, Object>> newEntries)
116    {
117        return new SynchronizableRepeater(newEntries != null ? newEntries : repeater.getEntries(), repeater.getPositionsMapping(), repeater.getReplacePositions(), repeater.getRemovedEntries(), repeater.getMode());
118    }
119    
120    /**
121     * Returns the mapping from stored positions to new ones.
122     * @return the mapping from stored positions to new ones.
123     */
124    public Map<Integer, Integer> getPositionsMapping()
125    {
126        return _positionsMapping;
127    }
128    
129    /**
130     * Returns the positions of replacing entries.
131     * @return the positions of replacing entries.
132     */
133    public List<Integer> getReplacePositions()
134    {
135        return _replacePositions;
136    }
137    
138    /**
139     * Returns the new repeater values.
140     * @return the new repeater values.
141     */
142    public List<Map<String, Object>> getEntries()
143    {
144        return _entries;
145    }
146    
147    /**
148     * Returns the entries to be removed. <br>
149     * Only relevant for append mode.<br>
150     * For replace mode, removed entries are carried trough the positions mapping.
151     * @return the new repeater values.
152     */
153    public Set<Integer> getRemovedEntries()
154    {
155        return _removedEntries;
156    }
157    
158    /**
159     * Retrieves the previous position of the given one from the repeater mapping
160     * @param newPosition the new entry position
161     * @return the previous entry position
162     */
163    public Optional<Integer> getPreviousPosition(int newPosition)
164    {
165        Map<Integer, Integer> mapping = getPositionsMapping();
166        if (mapping != null)
167        {
168            for (Map.Entry<Integer, Integer> mappingEntry : mapping.entrySet())
169            {
170                if (mappingEntry.getValue().equals(newPosition))
171                {
172                    return Optional.of(mappingEntry.getKey());
173                }
174            }
175        }
176        
177        return Optional.empty();
178    }
179    
180    /**
181     * Returns the write mode for provided entries.
182     * @return the write mode
183     */
184    public Mode getMode()
185    {
186        return _mode;
187    }
188}