Эффективная работа с файлами конфигурации

Status
Not open for further replies.

fromgate

Administrator
Некоторое время назад, я представил плагин Yamler (порт одноименного плагина с платформы Spigot/Bukkit, который позволяет организовать удобную работу с файлами конфигурации.
Предлагаемый им способ гораздо удобнее стандартных методов как в bukkit (класс YamlConfiguration) так и в nukkit (класс Config).

Однако у него есть огромный недостаток: для работы с конфигом приходится устанавливать сторонний плагин. А это сами понимаете, не самая интересная идея.

Поэтому меня "глодала мысль" создать нечто подобное на основе станадртных классов: опробовав удобство Yamler, меня уже неустраивала громоздскость обычных конфигураторов и написание плагинов на их основе просто стало мукой ;)

В итоге, в процессе написания плагина Regions (я надеюсь, скоро он будет готов), я написал небольшой класс который позволяет работать с конфигурацией способом подобным способам

Итак вот сам класс - его надо будет просто создать в любом пакете Вашего плагина:
Java:
import cn.nukkit.utils.Config;
 
import java.io.File;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
 
public abstract class YamlConfig {
 
	public boolean save(File f){
		f.getParentFile().mkdirs();
		try {
			if (f.exists()) f.delete();
			f.createNewFile();
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
		Config cfg = new Config(f, Config.YAML);
 
		for (Field field : this.getClass().getDeclaredFields()) {
			String path = getPath(field);
			try {
				cfg.set(path,field.get(this));
			} catch (IllegalAccessException e) {
				e.printStackTrace();
				return false;
			}
		}
		cfg.save();
		return true;
	}
 
	public boolean load(File f){
		f.getParentFile().mkdirs();
		try {
			if (!f.exists()) f.createNewFile();
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
		Config cfg = new Config(f, Config.YAML);
 
		for (Field field : this.getClass().getDeclaredFields()) {
			String path = getPath(field);
			try {
				if (field.getType()==int.class||field.getType()==Integer.class)
					field.set(this, cfg.getInt(path, field.getInt(this)));
				else if (field.getType()==boolean.class||field.getType()==Boolean.class)
					field.set(this,cfg.getBoolean(path,field.getBoolean(this)));
				else if (field.getType() == String.class)
					field.set(this,cfg.getString(path, (String) field.get(this)));
				else throw new UnsupportedClassVersionError("YamlConfig did not supports this class: "+field.getType().getName());
			} catch (Exception e) {
				e.printStackTrace();
				return false;
			}
 
		}
		return false;
	}
 
	private String getPath (Field field){
		String path = null;
		if (field.isAnnotationPresent(Path.class)) {
			Path pathDefine = field.getAnnotation(Path.class);
			path = pathDefine.value();
		}
		if (path == null||path.isEmpty()) field.getName().replaceAll("_", ".");
 
		if (Modifier.isPrivate(field.getModifiers())) {
			field.setAccessible(true);
		}
		return path;
	}
 
 
	@Retention(RetentionPolicy.RUNTIME)
	@Target(ElementType.FIELD)
	public @interface Path {
		String value() default "";
	}
 
 
}
Использовать это класс очень просто. Создаем класс, в котором будет храниться конфигурация плагина:
Java:
package ru.nukkit.regions.config;
 
public class RegionsConfig extends YamlConfig {
 
	@Path (value = "claim.max-regions-per-player")
	public int maxRegionPerPlayer=5;
 
	@Path (value = "claim.max-claim-volume")
	public int maxClaimVolume=10000;
 
	@Path (value = "claim.claim-only-existing-regions")
	public boolean claimOnlyExisting;
 
}
Полученный класс-конфигуратор, тоже просто в использовании.
Переменные — это обычные переменные, к которым ожно обращаться или напрямую или (если хочется сделать их private) посредством геттеров/сеттеров.
Java:
		cfg.load(new File(this.getDataFolder(),"config.yml"));
		cfg.claimOnlyExisting=true;
		cfg.maxClaimVolume=5000;
		cfg.save(new File(this.getDataFolder(),"config.yml"));
Т.е. фактически нужно создать объект класс-конфигуратора. Выполнить метод load() для загрузки настроек, после чего нужно просто обращаться к полям файла.

Если же нужно сохранить изменения в конфиге, то надо просто изменить поля в конфигураторе и вызвать метод save.

Использование этого подхода имеет ряд преимуществ:
  • Параметры конфигурации хранятся непосредственно в переменных, а не в полях коллекции LinkedHashMap. Т.е. если мерить наносекундами, то обращение к такой переменной будет выполняться быстрее.
  • Доступ к переменной, изменение его значения означает обычное обращение к полям объекта. Название полей конфига указывается лишь в виде аннотации (@Path (value="xxx.xxx)
  • Описать класс наследующий YamlConfig гораздо проще и быстрее чем традиционная работа со стандартным конфигуратором.
На текущий момент времени конфигуратор поддерживает лишь типы int, boolean, String - как самые распространенные. Но в процессе расширения функционала, я размещу его на гитхабе для свободного использования.
 
Last edited:

fromgate

Administrator
В общем повозившись немного вчера, я подумал, что такой класс вообще было бы неплохо включить в Nukkit.
Сделал пулл-реквест с предложением разработчиков. Посмотрим, одобрят или нет ;)
 

fromgate

Administrator
Пулл-реквест вроде принят.
Т.е. для последней версии Nukkit отдельные классы будут не нужны. Эту тему закрою, чуть позже опишу как работать с конфигами "по-новому" ;)
 
Status
Not open for further replies.
Top