Хостинг серверов Minecraft playvds.com
  1. Вы находитесь в русском сообществе Bukkit. Мы - администраторы серверов Minecraft, разрабатываем собственные плагины и переводим на русский язык плагины наших собратьев из других стран.
    Скрыть объявление

Стартап [ПЕРЕВОД] Предотвращение перезагрузки конфигурационных файлов из-за табуляций

Тема в разделе "Разработка плагинов для новичков", создана пользователем Dexel, 31 май 2017.

  1. Автор темы
    Dexel

    Dexel Активный участник Пользователь

    Баллы:
    76
    Skype:
    the_osirius
    Цель
    Как разработчик плагина с config.yml, вам может показаться знакомым то чувство, когда оптимистичные пользователи говорят вам, что весь их config.yml был сброшен к начальным настройкам. С фейспалмом на лице вы понимаете, что этого могло бы и не произойти, если бы пользователь просто использовал пробелы вместо символов табуляции.

    Так что же надо сделать? В этом туториале вы научитесь прослушивать символы табуляции, чтобы избежать сбросов. Нет больше онлайн-парсеров YAML! Ваш плагин сможет эффективно и просто обрабатывать такие проблемы.

    Прежде, чем мы начнём
    Очень рекомендуется иметь эти навыки у себя за поясом, чтобы понимать, что мы будем делать.
    Давайте начнём!

    Настройка вашего Класса
    Мы будем использовать главный класс для установки нашего "детектора табуляций". Для этого туториала мы пропустим настройку plugin.yml.
    Код:
    /**
    * Главный класс будет называться YamlTabParser. Замените названием своего главного
    * класса!
    *
    */
    public class YamlTabParser extends JavaPlugin {}
    Основные манипуляции
    Затем нам нужно указать две переменные:
    Код:
    public class YamlTabParser extends JavaPlugin {
    
        /*
         * Эти две переменные являются указателями на файл config.yml, который мы
         * создадим. Это поля, потому что у них очень широких охват. Они должны
         * обрабатываться надлежащим образом во время операций по сохранению и загрузке.
         */
        private File file;
        private FileConfiguration config;
    }
    Теперь фактически нам нужно перезаписать метод onEnable().
    Код:
        @Override
        public void onEnable() {
            // указываем переменные
            // ОБРАТИТЕ ВНИМАНИЕ: Вы не можете использовать конструктор в главном классе! Это
            // крашнет ваш плагин!
            file = new File(getDataFolder(), "config.yml");
            config = new YamlConfiguration();
    
            // перезагружаем и сохраняем файлы, мы перепишем реализацию JavaPlugin
            // в будущем.
            reloadConfig();
            saveConfig();
        }
    Перезапись saveConfig() и getConfig()
    После того, как мы настроили наш onEnable(), мы должны перезаписать операции saveConfig() и getConfig(), чтобы они соответствовали указанным нами ранее переменным.
    Код:
        /**
         * Перезаписывает возвратом нашей указанной переменной FileConfiguration
         */
        @Override
        public FileConfiguration getConfig() {
            return config;
        }
    
        /**
         * Перезаписывает стандартную операцию сохранения сохранением указанных нами переменных File
         * и FileConfiguration.
         */
        @Override
        public void saveConfig() {
            try {
                config.save(file);
            }
            catch (IOException e) {
                // выводить стек-трейс, если предпочитаете
                getLogger().severe(
                        "Could not save config.yml due to: " + e.getMessage());
            }
        }
    Здесь нечего обсуждать, мы просто проводим базовые операции, чтобы поддерживать согласованность и обеспечить нашим переменным правильное сохранение.
    Совет: Используйте аннотацию @Override - таким образом, если вы сделаете ошибку в заголовке метода, компилятор даст вам об этом знать!

    Создание Сканера
    Если вы выполнили предыдущие шаги, вы готовы приступать к сути этого туториала: Сканирование файлов на табуляции.
    Если вы не работали со Сканерами, я очень рекомендую вам провести немного своего времени за их изучением.

    Наш заголовок метода — это void без параметров. Так как наши переменные File и FileConfiguration глобализированные, это самый эффективный подход.
    Код:
        /**
         * Открывает java.util.Scanner для сканирования конфига на наличие табуляций.
         */
        private void scanConfig() {}
    Объявление сканера

    Код:
        private void scanConfig() {
            // объявляем нашу переменную сканера
            Scanner scan = null;
        }

    Определение сканера
    Код:
        private void scanConfig() {
            Scanner scan = null;
            try {
                scan = new Scanner(file);
            }
            catch (FileNotFoundException e) {
                // эта ошибка появится, если файл не существует
                e.printStackTrace();
            }
        }

    Теперь нам нужно заложить ещё несколько фундаментов. Фактически, сканер считывает одну строку за раз, символ за символом. Пока мы читаем файл, мы хотим отслеживать две вещи: строку, на которой мы сейчас находимся в файле и содержимое этой строки.
    Фактически, нам не нужен номер строки; это просто удобство для пользователя. Если мы знаем номер строки и находим там табуляцию, мы можем сообщить пользователю, на какой строке находится этот символ табуляции!

    Код:
        private void scanConfig() {
            Scanner scan = null;
            try {
                scan = new Scanner(file);
    
                int row = 0;
                String line = "";
            }
            catch (FileNotFoundException e) {
                // эта ошибка появится, если файл не существует
                e.printStackTrace();
            }
        }

    Теперь нам нужно прочитать весь файл, строку за строкой. Цикл while является наиболее эффективным в этой ситуации. Пока файл имеет ещё одну строку, мы будем переназначать нашу локальную переменную line на следующую строку файла.
    Код:
        private void scanConfig() {
    
            Scanner scan = null;
            try {
                scan = new Scanner(file);
              
                int row = 0;
                String line = "";
              
                // итерируем через файл строку за строкой
                while (scan.hasNextLine()) {
                    line = scan.nextLine();
                    // добавляем в номер строки
                    row++;
                }
            }
            catch (FileNotFoundException e) {
                // эта ошибка появится, если файл не существует
                e.printStackTrace();
            }
        }

    Теперь мы можем просматривать, содержит ли какая-нибудь строка файла символ табуляции. В Java табуляции разбираются при помощи регулярных выражений. В этом туториале вам не понадобится знать всё о регулярных выражениях. Базовое понимание помогает, и вообще это полезно знать, но это не нужно! Регулярное выражение для табуляций — это '\t'. Каждый раз, когда в файле встречается табуляция, это будет разбираться как '\t'. Нас не интересует, где находятся табуляции в файле, нам важен сам факт их присутствия. Мы можем использовать String#indexOf(), чтобы выяснить, есть ли в строке табуляция.
    Код:
        private void scanConfig() {
    
            Scanner scan = null;
            try {
                scan = new Scanner(file);
    
                int row = 0;
                String line = "";
    
                // итерируем через файл строку за строкой
                while (scan.hasNextLine()) {
                    line = scan.nextLine();
                    // добавляем в номер строки
                    row++;
    
                    // Если найдена табуляция ... \t = табуляция в регулярных выражениях
                    if (line.indexOf("\t") != -1) {
                        /*
                         * Сказать пользователю, где находится табуляция! Здесь мы бросаем
                         * IllegalArgumentException. Причина будет объяснена ниже.
                         */
                        String error = ("Tab found in config-file on line # " + row + "!");
                        throw new IllegalArgumentException(error);
                    }
                }
            }
            catch (FileNotFoundException e) {
                // эта ошибка появится, если файл не существует
                e.printStackTrace();
            }
        }
    }

    Так как этот метод обрабатывает чтение файла, мы должны загружать файл, если там не найдено табуляции. Также мы должны закрыть сканер, чтобы избежать утечек памяти.
    Операция загрузки выбрасывает IOException и InvalidConfigurationException; нужно обрабатывать это отдельно.

    Код:
        /**
         * Открывает java.util.Scanner для сканирования конфига на наличие табуляций.
         */
        private void scanConfig() {
            // объявляем нашу переменную сканера
            Scanner scan = null;
            try {
                scan = new Scanner(file);
    
                int row = 0;
                String line = "";
    
                // итерируем через файл строку за строкой
                while (scan.hasNextLine()) {
                    line = scan.nextLine();
                    // добавляем в номер строки
                    row++;
    
                    // Если найдена табуляция ... \t = табуляция в регулярных выражениях
                    if (line.indexOf("\t") != -1) {
                        /*
                         * Сказать пользователю, где находится табуляция! Здесь мы бросаем
                         * IllegalArgumentException.
                         */
                        String error = ("Tab found in config-file on line # " + row + "!");
                        throw new IllegalArgumentException(error);
                    }
                }
                /*
                 * загрузить файл, если были найдены табуляции, это не произойдёт
                 * из-за IllegalArgumentException
                 */
                config.load(file);
            }
            catch (FileNotFoundException e) {
                // эта ошибка появится, если файл не существует
                e.printStackTrace();
            }
            catch (IOException e) {
                // не удалось загрузить файл
                e.printStackTrace();
            }
            catch (InvalidConfigurationException e) {
                // ошибка shakeyaml - неверно настроен конфиг.
                e.printStackTrace();
            }
            finally {
                // Закрываем сканер, чтобы избежать утечек памяти.
                if (scan != null) {
                    scan.close();
                }
            }
        }
    Перезапись операции Перезагрузки

    Мы ещё не закончили! Нам нужно сканировать конфиг по двум причинам: когда плагин загружается и когда перезагружается. Мы вызываем reloadConfig() когда плагин включён, и reloadConfig() вызывается постоянно, когда в память записываются изменения. По существу, мы перепишем операцию перезагрузки, чтобы постоянно должным образом сканировать файл на наличие табуляций.
    Код:
        /**
         * Перезаписываем стандартную реализацию перезагрузки
         */
        @Override
        public void reloadConfig() {
            if (!file.exists()) {
                // Создаём файл, если он не существует. Изменения должным образом основываются на
                // вашей установке
                saveDefaultConfig();
            }
            scanConfig();
        }
    Итог
    Если вы правильно всё сделали, вы получили детектор табуляций YAML! Весь класс можно просмотреть на GitHub.

    Совет: Если вы хотите потокобезопасную операцию, не используйте Scanner. BufferedReader является синхронизированным и выполняет то же задание, но здесь я не буду возиться с его установкой. Bukkit-API в целом не является потокобезопасным, поэтому использовать эту операцию асинхронно нет смысла.

    Оригинал [ENG]
     
    Последнее редактирование: 31 май 2017
  2. Хостинг MineCraft
    <
  3. _MasterCapeXD_

    _MasterCapeXD_ Участник Пользователь

    Баллы:
    31
    Имя в Minecraft:
    _MasterCapeXD_
    :good::nerd:
     
  4. alexandrage

    alexandrage Администратор

    Баллы:
    173
    Skype:
    alexandr0116
    Костыль. Лучше научить нубов юзать notepat++. Там текст становится красным после таба, сложно незаметить. А еще лучше делать все редактирования на командах, тогда идиотам не придется конфиг ручками трогать.
     
  5. Dereku

    Dereku Старожил

    Баллы:
    173
    Skype:
    derek_unavailable
    Имя в Minecraft:
    _Dereku
    Зря перевёл не зная других способов. Да и мне кажется, что это для Java 6.
    Принёс говна, налетайте.
    Код:
    @Override
    public FileConfiguration getConfig() {
        File config = new File(this.getDataFolder(), "config.yml");
        if (!config.exists() || config.isDirectory()) {
            if (config.isDirectory()) {
                FileUtils.deleteQuietly(config);
            }
            this.saveDefaultConfig();
        }
        List<String> oldLines;
        try {
            oldLines = Files.readAllLines(config.toPath(), StandardCharsets.UTF_8);
        } catch (IOException ex) {
            this.getLogger().log(Level.WARNING, "Failed to read config.yml", ex);
            return null;
        }
    
        LinkedList<String> lines = new LinkedList<>();
        oldLines.stream().forEach((line) -> {
            lines.add(line.contains("\t") ? line.replace("\t", "    ") : line);
        });
     
        YamlConfiguration yamlConfiguration = new YamlConfiguration();
        try {
            yamlConfiguration.loadFromString(String.join("\n", lines));
        } catch (InvalidConfigurationException ex) {
            this.getLogger().log(Level.WARNING, "Failed to read configuration", ex);
            return null;
        }
        return yamlConfiguration;
    }
     
    Последнее редактирование: 1 июн 2017
  6. alexandrage

    alexandrage Администратор

    Баллы:
    173
    Skype:
    alexandr0116
    Скрысить чужие статьи проще, чем написать свое, это норма. Да еще и динозавродавности.
     

Поделиться этой страницей