001/*
002 * PermissionsEx
003 * Copyright (C) zml and PermissionsEx contributors
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *    http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package ca.stellardrift.permissionsex.impl.util;
018
019import org.pcollections.ConsPStack;
020import org.pcollections.HashTreePMap;
021import org.pcollections.HashTreePSet;
022import org.pcollections.PBag;
023import org.pcollections.PMap;
024import org.pcollections.PSet;
025import org.pcollections.PStack;
026import org.pcollections.PVector;
027import org.pcollections.TreePVector;
028
029import java.util.Collection;
030import java.util.Map;
031import java.util.function.BiFunction;
032import java.util.function.Function;
033import java.util.stream.Collector;
034
035/**
036 * Utilities for working with <a href="https://github.com/hrldcpr/pcollections">persistent collections</a>.
037 *
038 * <p>These are in three categories: converting from JDK collections to persistent ones,
039 * Stream helpers, and shorthand factory methods.</p>
040 */
041public final class PCollections {
042
043    private PCollections() {
044    }
045
046    // Conversions
047
048    public static <K, V> PMap<K, V> asMap(final Map<K, V> map) {
049        if (map instanceof PMap<?, ?>) {
050            return (PMap<K, V>) map;
051        } else {
052            return HashTreePMap.from(map);
053        }
054    }
055
056    public static <KI, VI, KO, VO> PMap<KO, VO> asMap(final Map<KI, VI> map, final BiFunction<KI, VI, KO> keyMapper, final BiFunction<KI, VI, VO> valueMapper) {
057        PMap<KO, VO> out = map();
058        for (final Map.Entry<KI, VI> entry : map.entrySet()) {
059            out = out.plus(
060                    keyMapper.apply(entry.getKey(), entry.getValue()),
061                    valueMapper.apply(entry.getKey(), entry.getValue())
062            );
063        }
064        return out;
065    }
066
067    // TODO: transforming methods for PMap?
068
069    public static <E> PSet<E> asSet(final Collection<E> set) {
070        if (set instanceof PSet<?>) {
071            return (PSet<E>) set;
072        } else {
073            return HashTreePSet.from(set);
074        }
075    }
076
077    public static <I, O> PSet<O> asSet(final Collection<I> list, final Function<? super I, ? extends O> xform) {
078        PSet<O> out = HashTreePSet.empty();
079        for (final I ent : list) {
080            out = out.plus(xform.apply(ent));
081        }
082        return out;
083    }
084
085    public static <E> PVector<E> asVector(final Collection<E> list) {
086        if (list instanceof PVector<?>) {
087            return (PVector<E>) list;
088        } else {
089            return TreePVector.from(list);
090        }
091    }
092
093    public static <I, O> PVector<O> asVector(final Collection<I> list, final Function<? super I, ? extends O> xform) {
094        PVector<O> out = TreePVector.empty();
095        for (final I ent : list) {
096            out = out.plus(xform.apply(ent));
097        }
098        return out;
099    }
100
101    public static <E> PStack<E> asStack(final Collection<E> stack) {
102        if (stack instanceof PStack<?>) {
103            return (PStack<E>) stack;
104        } else {
105            return ConsPStack.from(stack);
106        }
107    }
108
109    public static <I, O> PStack<O> asStack(final Collection<I> list, final Function<? super I, ? extends O> xform) {
110        PStack<O> out = ConsPStack.empty();
111        for (final I ent : list) {
112            out = out.plus(xform.apply(ent));
113        }
114        return out;
115    }
116
117    // Collectors
118
119    @SuppressWarnings({"unchecked", "rawtypes"})
120    public static <K, V> Collector<Map.Entry<K, V>, ?, PMap<K, V>> toPMap() {
121        return Collector.<Map.Entry<K, V>, PMap<K, V>[], PMap<K, V>>of(
122                () -> new PMap[] { HashTreePMap.empty() },
123                (arr, v) -> arr[0] = arr[0].plus(v.getKey(), v.getValue()),
124                (a, b) -> new PMap[] {a[0].plusAll(b[0])},
125                a -> a[0]
126        );
127    }
128
129    @SuppressWarnings({"unchecked", "rawtypes"})
130    public static <E> Collector<E, ?, PSet<E>> toPSet() {
131        return Collector.<E, PSet<E>[], PSet<E>>of(
132                () -> new PSet[] { HashTreePSet.empty() },
133                (arr, v) -> arr[0] = arr[0].plus(v),
134                (a, b) -> new PSet[] {a[0].plusAll(b[0])},
135                a -> a[0]
136        );
137    }
138
139    @SuppressWarnings({"unchecked", "rawtypes"})
140    public static <E> Collector<E, ?, PVector<E>> toPVector() {
141        return Collector.<E, PVector<E>[], PVector<E>>of(
142                () -> new PVector[] { TreePVector.empty() },
143                (arr, v) -> arr[0] = arr[0].plus(v),
144                (a, b) -> new PVector[] {a[0].plusAll(b[0])},
145                a -> a[0]
146        );
147    }
148
149    @SuppressWarnings({"unchecked", "rawtypes"})
150    public static <E> Collector<E, ?, PStack<E>> toPStack() {
151        return Collector.<E, PStack<E>[], PStack<E>>of(
152                () -> new PStack[] { ConsPStack.empty() },
153                (arr, v) -> arr[0] = arr[0].plus(v),
154                (a, b) -> new PStack[] {a[0].plusAll(b[0])},
155                a -> a[0]
156        );
157    }
158    
159    // narrowing -- valid because we have immutable collections
160    
161    @SuppressWarnings("unchecked")
162    public static <K1 extends K2, V1 extends V2, K2, V2> PMap<K2, V2> narrow(final PMap<K1, V1> input) {
163        return (PMap<K2, V2>) input;
164    }
165    
166    @SuppressWarnings("unchecked")
167    public static <E1 extends E2, E2> PSet<E2> narrow(final PSet<E1> input) {
168        return (PSet<E2>) input;
169    }
170
171    @SuppressWarnings("unchecked")
172    public static <E1 extends E2, E2> PVector<E2> narrow(final PVector<E1> input) {
173        return (PVector<E2>) input;
174    }
175
176    @SuppressWarnings("unchecked")
177    public static <E1 extends E2, E2> PStack<E2> narrow(final PStack<E1> input) {
178        return (PStack<E2>) input;
179    }
180
181    @SuppressWarnings("unchecked")
182    public static <E1 extends E2, E2> PBag<E2> narrow(final PBag<E1> input) {
183        return (PBag<E2>) input;
184    }
185
186    // factory methods //
187
188    public static <K, V> PMap<K, V> map() {
189        return HashTreePMap.empty();
190    }
191
192    public static <K, V> PMap<K, V> map(final K key, final V value) {
193        return HashTreePMap.singleton(key, value);
194    }
195
196    public static <E> PSet<E> set() {
197        return HashTreePSet.empty();
198    }
199
200    public static <E> PSet<E> set(final E element) {
201        return HashTreePSet.singleton(element);
202    }
203
204    @SafeVarargs
205    public static <E> PSet<E> set(final E... elements) {
206        PSet<E> out = set();
207        for (final E element : elements) {
208            out = out.plus(element);
209        }
210        return out;
211    }
212
213    public static <E> PVector<E> vector() {
214        return TreePVector.empty();
215    }
216
217    public static <E> PVector<E> vector(final E element) {
218        return TreePVector.singleton(element);
219    }
220
221    @SafeVarargs
222    public static <E> PVector<E> vector(final E... elements) {
223        PVector<E> out = vector();
224        for (final E element : elements) {
225            out = out.plus(element);
226        }
227        return out;
228    }
229
230    public static <E> PStack<E> stack() {
231        return ConsPStack.empty();
232    }
233
234    public static <E> PStack<E> stack(final E element) {
235        return ConsPStack.singleton(element);
236    }
237
238    @SafeVarargs
239    public static <E> PStack<E> stack(final E... elements) {
240        PStack<E> out = stack();
241        for (final E element : elements) {
242            out = out.plus(element);
243        }
244        return out;
245    }
246}