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}