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.backend.file;
018
019import ca.stellardrift.permissionsex.impl.backend.memory.MemorySubjectData;
020import ca.stellardrift.permissionsex.context.ContextValue;
021import ca.stellardrift.permissionsex.exception.PermissionsLoadingException;
022import ca.stellardrift.permissionsex.impl.util.PCollections;
023import org.pcollections.PMap;
024import org.pcollections.PSet;
025import org.spongepowered.configurate.ConfigurationNode;
026import org.spongepowered.configurate.serialize.SerializationException;
027
028import java.util.HashMap;
029import java.util.Map;
030import java.util.Set;
031
032public final class FileSubjectData extends MemorySubjectData {
033    static final String KEY_CONTEXTS = "contexts";
034
035    static FileSubjectData fromNode(ConfigurationNode node) throws SerializationException, PermissionsLoadingException {
036        PMap<PSet<ContextValue<?>>, MemorySegment> map = PCollections.map();
037        if (node.isList()) {
038            for (ConfigurationNode child : node.childrenList()) {
039                if (!child.isMap()) {
040                    throw new PermissionsLoadingException(Messages.FILE_LOAD_CONTEXT.tr());
041                }
042                final PSet<ContextValue<?>> contexts = contextsFrom(child);
043                MemorySegment value = MAPPER.load(child);
044                map = map.plus(contexts, value);
045            }
046        }
047        return new FileSubjectData(map);
048    }
049
050    FileSubjectData() {
051        super();
052    }
053
054    FileSubjectData(final PMap<PSet<ContextValue<?>>, MemorySegment> contexts) {
055        super(contexts);
056    }
057
058    @Override
059    protected MemorySubjectData newData(final PMap<PSet<ContextValue<?>>, MemorySegment> contexts) {
060        return new FileSubjectData(contexts);
061    }
062
063    private static PSet<ContextValue<?>> contextsFrom(final ConfigurationNode node) {
064        PSet<ContextValue<?>> contexts = PCollections.set();
065        final ConfigurationNode contextsNode = node.node(KEY_CONTEXTS);
066        if (contextsNode.isMap()) {
067            contexts = contextsNode.childrenMap().entrySet().stream()
068                    .map(ent -> new ContextValue<>(ent.getKey().toString(), ent.getValue().getString()))
069                    .collect(PCollections.toPSet());
070        }
071        return contexts;
072    }
073
074    void serialize(final ConfigurationNode node) throws SerializationException {
075        if (!node.isList()) {
076            node.raw(null);
077        }
078        final Map<Set<ContextValue<?>>, ConfigurationNode> existingSections = new HashMap<>();
079        for (final ConfigurationNode child : node.childrenList()) {
080            existingSections.put(contextsFrom(child), child);
081        }
082        for (final Map.Entry<PSet<ContextValue<?>>, MemorySegment> ent : this.segments.entrySet()) {
083            ConfigurationNode contextSection = existingSections.remove(ent.getKey());
084            if (contextSection == null) {
085                contextSection = node.appendListNode();
086                final ConfigurationNode contextsNode = contextSection.node(KEY_CONTEXTS);
087                for (final ContextValue<?> context : ent.getKey()) {
088                    contextsNode.node(context.key()).set(context.rawValue());
089                }
090            }
091            MAPPER.save(ent.getValue(), contextSection);
092        }
093        for (final ConfigurationNode unused : existingSections.values()) {
094            unused.raw(null);
095        }
096    }
097
098    @Override
099    public String toString() {
100        return "FileSubjectData{" +
101                "segments=" + segments +
102                '}';
103    }
104}