All Downloads are FREE. Search and download functionalities are using the official Maven repository.

kz.greetgo.scheduling.trigger.inner_logic.TriggerStructStrParser Maven / Gradle / Ivy

The newest version!
package kz.greetgo.scheduling.trigger.inner_logic;

import kz.greetgo.scheduling.trigger.atoms.SilentTrigger;
import kz.greetgo.scheduling.trigger.atoms.TriggerDayPoint;
import kz.greetgo.scheduling.trigger.atoms.TriggerMonth;
import kz.greetgo.scheduling.trigger.atoms.TriggerMonthDay;
import kz.greetgo.scheduling.trigger.atoms.TriggerPeriodInDay;
import kz.greetgo.scheduling.trigger.atoms.TriggerPeriodInDayRepeat;
import kz.greetgo.scheduling.trigger.atoms.TriggerRepeat;
import kz.greetgo.scheduling.trigger.atoms.TriggerWeekDay;
import kz.greetgo.scheduling.trigger.atoms.TriggerYear;
import kz.greetgo.scheduling.trigger.inner_logic.TriggerStructStrLexer.Lex;
import kz.greetgo.scheduling.util.TriggerUtil;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import static java.util.Collections.emptyList;
import static kz.greetgo.scheduling.trigger.inner_logic.LexType.AFTER_PAUSE;
import static kz.greetgo.scheduling.trigger.inner_logic.LexType.DIGIT;
import static kz.greetgo.scheduling.trigger.inner_logic.LexType.EVERY;
import static kz.greetgo.scheduling.trigger.inner_logic.LexType.FROM;
import static kz.greetgo.scheduling.trigger.inner_logic.LexType.MONTH;
import static kz.greetgo.scheduling.trigger.inner_logic.LexType.RANGE_DELIMITER;
import static kz.greetgo.scheduling.trigger.inner_logic.LexType.REPEAT;
import static kz.greetgo.scheduling.trigger.inner_logic.LexType.TIME_OF_DAY;
import static kz.greetgo.scheduling.trigger.inner_logic.LexType.TIME_VALUE;
import static kz.greetgo.scheduling.trigger.inner_logic.LexType.TO;
import static kz.greetgo.scheduling.trigger.inner_logic.LexType.WEEK_DAY;
import static kz.greetgo.scheduling.trigger.inner_logic.LexType.YEAR;

public class TriggerStructStrParser {

  private final Range  range;
  private final String source;

  private TriggerStructStrParser(Range range, String source) {
    this.range  = range;
    this.source = source;
  }

  public static TriggerStructStrParser of(Range range, String source) {
    return new TriggerStructStrParser(range, source);
  }

  public final List errorList = new ArrayList<>();

  Trigger trigger = null;

  public Trigger parse() {

    TriggerStructStrLexer lexer = new TriggerStructStrLexer(range, source);

    lexer.parse();

    if (lexer.errorList.size() > 0) {
      errorList.addAll(lexer.errorList);
      return new SilentTrigger();
    }

    parseLexList(lexer.lexList);

    if (lexer.errorList.size() > 0) {
      return new SilentTrigger();
    }

    if (trigger == null) {
      errorList.add(new ParseError(Range.of(0, source.length()), "214hY88", "Триггер не определён"));
      return new SilentTrigger();
    }

    return trigger;
  }

  private void parseLexList(List lexList) {

    if (lexList.isEmpty()) {
      errorList.add(new ParseError(range, "n1w2mk5", "Нет расписания"));
      return;
    }

    if (lexList.stream().anyMatch(x -> x.type == MONTH || x.type == YEAR)) {
      parseMonthOrYear(lexList);
      return;
    }

    int i = 0, len = lexList.size();

    while (i < len) {

      Lex lex = lexList.get(i);

      if (trigger != null) {

        if (trigger instanceof TriggerPeriodInDay && lex.type == EVERY) {

          if (i + 1 >= len) {
            errorList.add(new ParseError(lex.range(), "2135jh6", "Не указана частота повторений"));
            return;
          }

          Lex next = lexList.get(i + 1);

          if (next.type != TIME_VALUE) {
            errorList.add(new ParseError(next.range(), "1b4ydWQ", "Не та лексема - нужно указать частоту повторений"));
            return;
          }

          trigger = new TriggerPeriodInDayRepeat((TriggerPeriodInDay) trigger, next.readTimeValueMillis());

          i += 2;
          continue;

        }

        errorList.add(new ParseError(lex.range(), "2h4hY88", "Лишняя лексема - триггер уже определён"));
        return;
      }

      if (lex.type == REPEAT) {

        int step = 2;

        if (i + 1 >= len) {
          errorList.add(new ParseError(lex.range(), "2n4b6v7", "Не указано количество повторений"));
          return;
        }

        Lex next = lexList.get(i + 1);

        if (next.type != TIME_VALUE) {
          errorList.add(new ParseError(lex.range(), "2h355h4", "Нужно указать частоту повторений"));
          return;
        }

        long delayMillis = next.readTimeValueMillis();

        long startSilentMillis = 0;

        if (i + 2 < len) {

          Lex next2 = lexList.get(i + 2);

          if (next2.type == AFTER_PAUSE) {

            if (i + 3 >= len) {
              errorList.add(new ParseError(next2.range(), "b54n254", "Не указано время паузы"));
              return;
            }

            Lex next3 = lexList.get(i + 3);

            if (next3.type != TIME_VALUE) {
              errorList.add(new ParseError(lex.range(), "j3u5b6v", "Нужно указать величину паузы"));
              return;
            }

            step              = 4;
            startSilentMillis = next3.readTimeValueMillis();

          }

        }

        trigger = new TriggerRepeat(startSilentMillis, delayMillis);

        i += step;
        continue;

      }

      if (lex.type == FROM) {
        if (i + 1 >= len) {
          errorList.add(new ParseError(lex.range(), "nsy3u8w", "Не указано количество повторений"));
          return;
        }

        Lex fromValue = lexList.get(i + 1);

        if (fromValue.type != TIME_OF_DAY) {
          errorList.add(new ParseError(lex.range(), "n2u63h2", "Несогласованная лексема - ожидается время HH:mm[:ss]"));
          return;
        }

        if (i + 2 >= len) {
          errorList.add(new ParseError(lex.range(), "iqE72WW", "Не указано конечное время"));
          return;
        }

        Lex to = lexList.get(i + 2);

        if (to.type != TO) {
          errorList.add(new ParseError(fromValue.range(), "ws7iq92", "Несогласованная лексема - ожидается" +
            " указатель окончания временного интервала"));
          return;
        }

        if (i + 3 >= len) {
          errorList.add(new ParseError(to.range(), "qii543w", "Незаконченная лексема - ожидается время HH:mm[:ss]"));
          return;
        }

        Lex toValue = lexList.get(i + 3);

        if (toValue.type != TIME_OF_DAY) {
          errorList.add(new ParseError(lex.range(), "qiu2777", "Несогласованная лексема - ожидается время HH:mm[:ss]"));
          return;
        }

        long fromMillis = fromValue.readTimeOfDayInMillis();
        long toMillis   = toValue.readTimeOfDayInMillis();

        trigger = new TriggerPeriodInDay(fromMillis, toMillis);

        i += 4;
        continue;

      }

      if (lex.type == WEEK_DAY) {

        trigger = new TriggerWeekDay(lex.getWeekDay());

        i++;
        continue;
      }

      if (lex.type == TIME_OF_DAY) {

        trigger = new TriggerDayPoint(lex.tokens.get(0).str());

        i++;
        continue;
      }

      errorList.add(new ParseError(lex.range(), "j25bhj4", "Несогласованная лексема"));
      return;
    }

  }

  private boolean validateDoubleYears(List lexList) {
    boolean hasYear = false;
    for (final Lex lex : lexList) {
      if (lex.type == YEAR) {
        if (hasYear) {
          errorList.add(new ParseError(lex.range(), "uEIlY6GC5T", "Повторно указан год"));
          return true;
        }
        hasYear = true;
      }
    }
    return false;
  }

  private static  List reverseList(List list) {
    List reversedLexList = new ArrayList<>(list);
    Collections.reverse(reversedLexList);
    return reversedLexList;
  }

  private void parseMonthOrYear(List lexList) {

    if (validateDoubleYears(lexList)) {
      return;
    }

    List reversedLexList = reverseList(lexList);

    List reversedYears = new ArrayList<>();
    Lex       yearLex       = null;

    List reversedMonthDays = new ArrayList<>();
    List reversedMonths    = new ArrayList<>();
    boolean   monthsStopped     = false;

    final int DIGITS_TO_UNKNOWN = 0;
    final int DIGITS_TO_MONTHS  = 1;
    final int DIGITS_TO_YEARS   = 2;

    int digitsTo = DIGITS_TO_UNKNOWN;

    final int RANGE_TO_UNKNOWN    = 0;
    final int RANGE_TO_MONTH_DAYS = 1;
    final int RANGE_TO_MONTHS     = 2;
    final int RANGE_TO_YEARS      = 3;

    int rangeTo = RANGE_TO_UNKNOWN;

    for (final Lex lex : reversedLexList) {
      if (lex.type == MONTH) {
        if (monthsStopped) {
          errorList.add(new ParseError(reversedMonths.get(reversedMonths.size() - 1).range(),
                                       "A6Dy08k7CL", "Месяцы можно определять только один раз"));
          return;
        }
        reversedMonths.add(lex);
        digitsTo = DIGITS_TO_MONTHS;
        rangeTo  = RANGE_TO_MONTHS;
        continue;
      }

      if (lex.type == RANGE_DELIMITER) {
        if (rangeTo == RANGE_TO_MONTH_DAYS) {
          reversedMonthDays.add(lex);
        } else if (rangeTo == RANGE_TO_MONTHS) {
          reversedMonths.add(lex);
        } else if (rangeTo == RANGE_TO_YEARS) {
          reversedYears.add(lex);
        } else {
          errorList.add(new ParseError(lex.range(), "I2MpN0fzDY", "Неуместное положение диапазона"));
          return;
        }
        continue;
      }

      if (reversedMonths.size() > 0) {
        monthsStopped = true;
      }

      if (lex.type == YEAR) {
        if (yearLex != null) {
          errorList.add(new ParseError(lex.range(), "CfD5sdc37e", "Год можно определять только один раз"));
          return;
        }
        yearLex  = lex;
        digitsTo = DIGITS_TO_YEARS;
        rangeTo  = RANGE_TO_YEARS;
        continue;
      }

      if (rangeTo == RANGE_TO_MONTHS) {
        rangeTo = RANGE_TO_MONTH_DAYS;
      }

      if (digitsTo == DIGITS_TO_MONTHS) {
        reversedMonthDays.add(lex);
        continue;
      }
      if (digitsTo == DIGITS_TO_YEARS) {
        reversedYears.add(lex);
        continue;
      }

      {
        errorList.add(new ParseError(lex.range(), "WH3JGs16xh", "Неизвестное назначение лексемы"));
        return;
      }
    }

    List years     = reverseList(reversedYears);
    List monthDays = reverseList(reversedMonthDays);
    List months    = reverseList(reversedMonths);

    Trigger yearTrigger = null;

    if (yearLex != null) {

      if (years.isEmpty()) {
        errorList.add(new ParseError(yearLex.range(), "WH3JGs16xh", "Не указано значение года"));
        return;
      }

      yearTrigger = extractRanges(years, DIGIT).stream()
                                               .map(TriggerYear::new)
                                               .map(Trigger.class::cast)
                                               .reduce(TriggerUtil::or)
                                               .orElse(SilentTrigger.SILENT);
    }

    Trigger monthTrigger = null;

    if (months.size() > 0) {
      monthTrigger = createMonthsTrigger(months, monthDays);
    }

    if (errorList.size() > 0) {
      return;
    }

    if (yearTrigger != null && monthTrigger != null) {
      trigger = TriggerUtil.and(monthTrigger, yearTrigger);
      return;
    }

    if (yearTrigger != null) {
      trigger = yearTrigger;
      return;
    }

    if (monthTrigger != null) {
      trigger = monthTrigger;
      return;
    }

    {
      errorList.add(new ParseError(range, "p4bl3D6LFC", "Ничего нет"));
      return;
    }
  }

  private List extractRanges(List digitRangeLaxList, LexType sourceType) {

    Lex leftLex = digitRangeLaxList.stream()
                                   .filter(x -> x.type != sourceType && x.type != RANGE_DELIMITER)
                                   .findAny()
                                   .orElse(null);

    if (leftLex != null) {
      errorList.add(new ParseError(leftLex.range(),
                                   "RR9FP7bRuO", "Неуместная лексема: возможны только лексемы" +
                                     " типов: " + sourceType + ", " + RANGE_DELIMITER));
      return emptyList();
    }

    List ret = new ArrayList<>();

    List list = new ArrayList<>(digitRangeLaxList);

    while (list.size() > 0) {

      Lex first = list.remove(0);
      if (first.type == RANGE_DELIMITER) {
        errorList.add(new ParseError(first.range(),
                                     "xT5Kl13yda", "Диапазон без значения слева"));
        return emptyList();
      }

      if (list.isEmpty()) {
        ret.add(createRange(first, first, sourceType));
        break;
      }

      if (list.get(0).type != RANGE_DELIMITER) {
        ret.add(createRange(first, first, sourceType));
        continue;
      }

      Lex rangeLex = list.remove(0);

      if (list.isEmpty()) {
        errorList.add(new ParseError(rangeLex.range(),
                                     "S7bGQgQ70z", "Диапазон без значения справа"));
        return emptyList();
      }

      Lex second = list.remove(0);

      if (second.type == RANGE_DELIMITER) {
        errorList.add(new ParseError(second.range(),
                                     "aed7eLG3yd", "Повторный диапазон - уберите один"));
        return emptyList();
      }

      ret.add(createRange(first, second, sourceType));

    }

    return ret;

  }

  private static Range createRange(Lex from, Lex to, LexType sourceType) {
    if (from.type != sourceType || to.type != sourceType) {
      throw new RuntimeException("8B9X6wqGnF :: Both must have type " + sourceType + ": from=" + from + ", to=" + to);
    }

    int intFrom = extractIntFromLex(from);
    int intTo   = extractIntFromLex(to);

    return new Range(intFrom, intTo);
  }

  private static int extractIntFromLex(Lex lex) {
    switch (lex.type) {
      default:
        throw new RuntimeException("KUt77u2COr :: Cannot extract int from lex with type = " + lex.type);

      case DIGIT:
        return Integer.parseInt(lex.tokens.get(0).str());

      case MONTH:
        return TriggerStructStrLexer.readMonth(lex.tokens.get(0).strNormy());
    }
  }


  private Trigger createMonthsTrigger(List months, List monthDays) {

    Trigger monthTrigger = extractRanges(months, MONTH).stream()
                                                       .map(TriggerMonth::new)
                                                       .map(Trigger.class::cast)
                                                       .reduce(TriggerUtil::or)
                                                       .orElse(SilentTrigger.SILENT);

    if (monthDays.isEmpty()) {
      return monthTrigger;
    }

    Trigger monthDayTrigger = extractRanges(monthDays, DIGIT).stream()
                                                             .map(TriggerMonthDay::new)
                                                             .map(Trigger.class::cast)
                                                             .reduce(TriggerUtil::or)
                                                             .orElse(SilentTrigger.SILENT);


    return TriggerUtil.and(monthDayTrigger, monthTrigger);
  }


}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy