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;
018
019import ca.stellardrift.permissionsex.PermissionsEngine;
020import ca.stellardrift.permissionsex.PermissionsEngineBuilder;
021import ca.stellardrift.permissionsex.exception.PermissionsLoadingException;
022import ca.stellardrift.permissionsex.impl.config.FilePermissionsExConfiguration;
023import com.google.auto.service.AutoService;
024import io.leangen.geantyref.TypeToken;
025import org.checkerframework.checker.nullness.qual.Nullable;
026import org.slf4j.Logger;
027import org.slf4j.LoggerFactory;
028import org.spongepowered.configurate.ConfigurateException;
029import org.spongepowered.configurate.gson.GsonConfigurationLoader;
030import org.spongepowered.configurate.hocon.HoconConfigurationLoader;
031import org.spongepowered.configurate.loader.ConfigurationLoader;
032import org.spongepowered.configurate.util.CheckedFunction;
033import org.spongepowered.configurate.util.UnmodifiableCollections;
034import org.spongepowered.configurate.yaml.NodeStyle;
035import org.spongepowered.configurate.yaml.YamlConfigurationLoader;
036
037import javax.sql.DataSource;
038import java.nio.file.Path;
039import java.nio.file.Paths;
040import java.sql.SQLException;
041import java.util.Map;
042import java.util.concurrent.Executor;
043import java.util.concurrent.ForkJoinPool;
044import java.util.function.Supplier;
045
046import static java.util.Objects.requireNonNull;
047import static net.kyori.adventure.text.Component.text;
048
049public final class PermissionsExBuilder<C> implements PermissionsEngineBuilder<C> {
050    final Class<C> configType;
051    @Nullable Path configFile;
052    @Nullable Path baseDirectory;
053    @Nullable Logger logger;
054    @Nullable Executor asyncExecutor;
055    CheckedFunction<String, @Nullable DataSource, SQLException> databaseProvider = $ -> null;
056
057    PermissionsExBuilder(final Class<C> configType) {
058        this.configType = configType;
059    }
060
061    @Override
062    public PermissionsEngineBuilder<C> configuration(final Path configFile) {
063        this.configFile = configFile;
064        return this;
065    }
066
067    @Override
068    public PermissionsEngineBuilder<C> baseDirectory(final Path baseDir) {
069        this.baseDirectory = baseDir;
070        return this;
071    }
072
073    @Override
074    public PermissionsEngineBuilder<C> logger(final Logger logger) {
075        this.logger = requireNonNull(logger, "logger");
076        return this;
077    }
078
079    @Override
080    public PermissionsEngineBuilder<C> asyncExecutor(final Executor executor) {
081        this.asyncExecutor = requireNonNull(executor, "executor");
082        return this;
083    }
084
085    @Override
086    public PermissionsEngineBuilder<C> databaseProvider(final CheckedFunction<String, @Nullable DataSource, SQLException> databaseProvider) {
087        this.databaseProvider = requireNonNull(databaseProvider, "databaseProvider");
088        return this;
089    }
090
091    @Override
092    public PermissionsEx<?> build() throws PermissionsLoadingException {
093        return (PermissionsEx<?>) this.buildWithConfig().getKey();
094    }
095
096    @Override
097    public Map.Entry<PermissionsEngine, Supplier<C>> buildWithConfig() throws PermissionsLoadingException {
098        if (this.logger == null) {
099            this.logger = LoggerFactory.getLogger("PermissionsEx");
100        }
101
102        if (this.asyncExecutor == null) {
103            this.asyncExecutor = ForkJoinPool.commonPool();
104        }
105
106        if (this.configFile == null) {
107            throw new PermissionsLoadingException(text("Configuration file has not been set"));
108        }
109
110        if (this.baseDirectory == null) {
111            this.baseDirectory = Paths.get(".");
112        }
113
114        final FilePermissionsExConfiguration<C> config = makeConfiguration(this.configFile);
115        final PermissionsEx<C> engine = new PermissionsEx<>(
116            this.logger,
117            this.baseDirectory,
118            this.asyncExecutor,
119            this.databaseProvider
120        );
121
122        engine.initialize(config);
123        return UnmodifiableCollections.immutableMapEntry(engine, engine.config()::getPlatformConfig);
124    }
125
126    private FilePermissionsExConfiguration<C> makeConfiguration(final Path configFile) throws PermissionsLoadingException {
127        final String fileName = configFile.getFileName().toString();
128        final ConfigurationLoader<?> loader;
129        if (fileName.endsWith(".yml") || fileName.endsWith(".yaml")) {
130            loader = YamlConfigurationLoader.builder()
131                .path(configFile)
132                .nodeStyle(NodeStyle.BLOCK)
133                .defaultOptions(FilePermissionsExConfiguration.PEX_OPTIONS)
134                .build();
135        } else if (fileName.endsWith(".conf") || fileName.endsWith(".hocon")) {
136            loader = HoconConfigurationLoader.builder()
137                .path(configFile)
138                .defaultOptions(FilePermissionsExConfiguration.PEX_OPTIONS)
139                .build();
140        } else if (fileName.endsWith(".json")) {
141            loader = GsonConfigurationLoader.builder()
142                .indent(2)
143                .defaultOptions(FilePermissionsExConfiguration.PEX_OPTIONS)
144                .build();
145        } else {
146            throw new PermissionsLoadingException(Messages.CONFIG_ERROR_UNKNOWN_FORMAT.tr(fileName));
147        }
148
149        try {
150            return FilePermissionsExConfiguration.fromLoader(loader, this.configType);
151        } catch (final ConfigurateException ex) {
152            throw new PermissionsLoadingException(Messages.CONFIG_ERROR_LOAD.tr(ex));
153        }
154    }
155
156    @AutoService(PermissionsEngineBuilder.Factory.class)
157    public static class Factory implements PermissionsEngineBuilder.Factory {
158
159        @Override
160        public <C> PermissionsEngineBuilder<C> newBuilder(final Class<C> configType) {
161            return new PermissionsExBuilder<>(configType);
162        }
163
164    }
165
166}