Ru.Mami.FileCache
package ru.mami;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public final class FileCache {
private Pattern pattern;
private int targetGroupIndex;
private Path file;
public FileCache(String fileAddress) {
// Получение Path из указанного адреса
file = Paths.get(fileAddress);
// создание шаблона из стандартного регулярного выражения (что угодно
// кроме '|')
// () - группа
// [символы] - любой из символов внутри квадратных скобок
// [^символы] - любой символ, кроме тех, которые находятся внутри
// квадратных скобок
// * - ноль или более совпадений
// [^|]* - ноль или более любых символов, кроме '|'
// $ - конец строки
// \\| - символ '|', экранированный при помощи \\, чтобы сообщить
// компилятору, что это именно '|', а не 'или', так как вне квадратных
// скобок многие символы (в том числе этот) имеют специальное значение
// ([^|]*)($|\\|) - ноль или более любых символов, кроме '|', сразу
// после которых идет либо '|', либо конец строки
pattern = Pattern.compile("([^|]*)($|\\|)");
// индекс группы, в которой находятся нужные данные (0 - все что совпало
// с регулярным выражением, 1 и далее - круглые скобки по порядку)
targetGroupIndex = 1;
}
public FileCache(String fileAddress, String regex, int targetGroupIndex) {
file = Paths.get(fileAddress);
// создание шаблона из предоставленного регулярного выражения
pattern = Pattern.compile(regex);
this.targetGroupIndex = targetGroupIndex;
}
/**
* Поиск совпадения в кэше (поиск в файле, заданном при создании класса,
* строки, в которой первые несколько подстрок, соответствующие шаблону,
* заданному при создании класса, в точности совпадают с предоставленными
* значениями
*
* Метод написан для вынесения общего куска кода из методов loadOne и
* loadAll
*
* @param values
* значения, которые должны совпасть, чтобы запись в кэше
* считалась найденной (параметры задачи)
* @return объект класса Matcher, указывающий на подстроку, совпавшую с
* последним значением в values (т.о. следующий вызов matcher.find()
* передвинет указатель на первый результат вычисления) или null,
* если строки найти не удалось
*/
private Matcher findMatch(String... values) {
// если такого файла нет, сразу вернуть null
if (!Files.exists(file)) {
return null;
}
try {
// прочитать все строки файла в список строк
List<String> lines = Files.readAllLines(file);
// для каждой строки
for (String line : lines) {
// создать Matcher
Matcher matcher = pattern.matcher(line);
// начинать искать совпадения с первого элемента массива values
int index = 0;
// пока индекс текущего элемента массива values на превысил
// количество элементов массива (index < values.length)
// и существует следующая подстрока, соответствующая шаблону
// (matcher.find())
// и эта подстрока в точности соответствует текущему элементу
// массива values (matcher.group().equals(values[index]))
while (index < values.length && matcher.find()
&& matcher.group(targetGroupIndex)
.equals(values[index])) {
// переходить к следующему элементу
++index;
}
// если для всех элементов массива нашлось совпадение
if (index == values.length) {
// вернуть этот Matcher
return matcher;
}
}
// если ни в одной строке совпадения не нашлись, вернуть null
return null;
} catch (IOException e) {
// если возникла ошибка при чтении файла, вернуть null
return null;
}
}
/**
* Поиск совпадения в кэше (поиск в файле, заданном при создании класса,
* строки, в которой первые несколько подстрок, соответствующие шаблону,
* заданному при создании класса, в точности совпадают с предоставленными
* значениями)
*
* @param values
* значения, которые должны совпасть, чтобы запись в кэше
* считалась найденной (параметры задачи)
* @return список (List<String>) всех остальных подстрок, соответствующих
* шаблону, найденных в этой строке (результаты решения задачи), или
* null, если такой строки обнаружить не удалось
*/
public List<String> loadAll(String... values) {
// получить Matcher из функции findMatch
Matcher matcher = findMatch(values);
// если возвращенное значение не null и у матчера есть другие подстроки,
// совпавшие с шаблоном
if (matcher != null && matcher.find()) {
// создать новый список
List<String> result = new LinkedList<String>();
// поместить в него все подстроки из матчера
do {
result.add(matcher.group(targetGroupIndex));
} while (matcher.find());
return result;
}
// иначе вернуть null
return null;
}
/**
* Поиск совпадения в кэше (поиск в файле, заданном при создании класса,
* строки, в которой первые несколько подстрок, соответствующие шаблону,
* заданному при создании класса, в точности совпадают с предоставленными
* значениями)
*
* Метод написан для удобства, чтобы не работать со списком, состоящим из
* одного элемента
*
* @param values
* значения, которые должны совпасть, чтобы запись в кэше
* считалась найденной (параметры задачи)
* @return одна подстрока, соответствующая шаблону, идущая в этой строке
* сразу после параметров (единственый результат решения задачи),
* или null, если такой строки обнаружить не удалось
*/
public String loadOne(String... values) {
// получить Matcher из функции findMatch
Matcher matcher = findMatch(values);
// если возвращенное значение не null и у матчера есть другие подстроки,
// совпавшие с шаблоном
if (matcher != null && matcher.find()) {
// вернуть следующую подстроку из матчера
return matcher.group(targetGroupIndex);
}
// иначе вернуть null
return null;
}
/**
* Сохранение значения в кэше
*
* @param values
* значения, которые надо сохранить (параметры и результаты
* решения задачи)
*/
public void save(String... values) {
// объединить полученный массив строк в одну строку, разделив все
// значения символом '|'
String line = String.join("|", values);
try {
// дописать эту строку в файл
Files.write(file, Arrays.asList(line),
StandardOpenOption.CREATE,
StandardOpenOption.APPEND);
} catch (IOException e) {
e.printStackTrace();
}
}
}