예제 #1
0
        /// <summary>
        /// Преобразовать двусвязный список шаблонов железной дороги в модель по ТЗ
        /// </summary>
        /// <param name="head">Указатель на начало списка</param>
        /// <param name="initial">Исходные данные модели (блоки DATA, ROUTES)</param>
        /// <returns></returns>
        public static Model ConvertToModel(this IRailwayTemplate head, Model initial = null)
        {
            // Копируем исходные данные
            var model = Model.Copy(initial);

            model.Topology.Clear();
            model.Order.Clear();

            var k        = 0;
            var template = head;

            do
            {
                // Разбиваем шаблон железной дороги на отдельные блоки
                var railways = template.GetRailways();
                for (var i = 0; i < railways.Count; i++)
                {
                    var railway = railways[i];

                    if (railway.Type == RailwayType.L0)
                    {
                        continue;
                    }

                    var source      = k + i - 1;
                    var destination = source + 1;

                    // Добавляем каждый блок в модель
                    model.Order.Add(railway.Name);

                    var topology = new TopologyItem(source, destination, railway.Direction);
                    model.Topology.Add(topology);
                }

                template = template.Next;
                k       += railways.Count;

                if (template == null)
                {
                    var topology = new TopologyItem(k - 1, k, railways.Last().Direction);
                    model.Topology.Add(topology);
                }
            }while (template != null);

            model.Topology.Last().SecondBlock = 0;

            return(model);
        }
예제 #2
0
파일: Loader.cs 프로젝트: Berethor/Lillabu
        /// <summary>
        /// Разбор и проверка входных данных
        /// </summary>
        /// <param name="text">Содержимое входных данных</param>
        /// <returns>Преобразованная информация в объект данных и список ошибок</returns>
        /// <exception cref="FormatException">Исключение, в сообщении которого находится информация об ошибке</exception>
        public Tuple <Model, Exception[]> CheckAndParse(string text)
        {
            var exceptions = new List <Exception>();

            var model = new Model(MAX_ELEMENTS_RESTRICTION, MAX_POINTS_RESTRICTION);

            try
            {
                var lines = text.Replace("\r", "").Split('\n');

                var blockReached = new Dictionary <string, bool>();

                int elementsCount = 0;
                int pointsCount   = 0;

                // Блок, который считывается в данный момент
                KeyValuePair <int, string>?currentBlock = null;
                int blockIndex = -1;

                // Был ли прочитанный блок пустым?
                bool isCurrentBlockNonEmpty = false;
                // Была ли встречена строка, закрывающая блок?
                bool isEndBlockReached = false;

                // Считываем файл построчно
                for (int lineNum = 1; lineNum <= lines.Length; lineNum++)
                {
                    var line = lines[lineNum - 1];

                    string exMessageTemplate = string.Format(EXCEPTION_TEMPLATE, lineNum, "{0}");

                    // Находим и удаляем комментарии в строке
                    var commentIndex = line.IndexOf(COMMENT_SIGN);
                    if (commentIndex != -1)
                    {
                        line = line.Remove(commentIndex);
                    }
                    line = line.Trim();

                    // Пропускаем строки-комментарии
                    if (string.IsNullOrEmpty(line))
                    {
                        continue;
                    }

                    // Проверяем данную строку на объявление начала блока и их посследовательность
                    if ((blockIndex = _keyWords.IndexOf(line.ToUpper())) != -1)
                    {
                        // Первый вариант с последовательно расположенными блоками
                        // Может пригодится
                        //if ((currentBlock?.Key ?? -1) != (blockIndex - 1))
                        //{
                        //    throw new FormatException(string.Format(exMessageTemplate, $"{_keyWords[blockIndex]} block missing"));
                        //}

                        // Объявление нового блока данных без закрытия предыдущего
                        if ((currentBlock.HasValue) && (!isEndBlockReached))
                        {
                            exceptions.Add(new FormatException(string.Format(exMessageTemplate, $"{_keyWords[blockIndex - 1]} block should be closed with \"{END_BLOCK_SIGN}\" line")));
                        }

                        // Проверка на наличие данных
                        if (currentBlock.HasValue && !isCurrentBlockNonEmpty)
                        {
                            exceptions.Add(new FormatException(string.Format(exMessageTemplate, $"{_keyWords[blockIndex - 1]} block should not be empty")));
                        }

                        currentBlock = new KeyValuePair <int, string>(blockIndex, line.ToUpper());

                        isCurrentBlockNonEmpty = false;
                        isEndBlockReached      = false;

                        if (!blockReached.ContainsKey(currentBlock.Value.Value))
                        {
                            blockReached.Add(currentBlock.Value.Value, true);
                        }

                        continue;
                    }

                    // Нет объявления блока
                    if (currentBlock == null)
                    {
                        exceptions.Add(new FormatException(string.Format(exMessageTemplate, $"Wrong block declaration. It is should be one of ({string.Join(",", _keyWords)})")));
                    }

                    if (line.Equals(END_BLOCK_SIGN))
                    {
                        // Добавляем координату 0-0, если она не является последней координатой

                        /*
                         * if (model.Points.Count != 0)
                         * {
                         *  var point = new Point(0, 0);
                         *  if (!model.Points.Last().Equals(point))
                         *  {
                         *      model.Points.Add(point);
                         *  }
                         * }
                         */
                        isEndBlockReached = true;
                        continue;
                    }

                    // Разбираем сами данные
                    var data = line.Trim().Replace(".", ",").Split(DATA_SEPARATOR_BASIC, DATA_SEPARATOR_EXPAND);
                    var wrongArgcExTemplate      = string.Format(exMessageTemplate, "Wrong number of input args for {0} block. Should be {1} but was {2}");
                    var wrongArgFormatExTemplate = string.Format(exMessageTemplate, "Argument has wrong format. {0}");

                    switch (currentBlock.Value.Value)
                    {
                    case DATA_KEY_WORD:
                        checkBlockLength(DATA_BLOCK_ARGC, data.Length, currentBlock.Value.Value, wrongArgcExTemplate);

                        var dataPointName = data[0];

                        checkPointName(dataPointName, wrongArgFormatExTemplate);

                        if (!int.TryParse(data[1], out int count))
                        {
                            exceptions.Add(new FormatException(string.Format(wrongArgFormatExTemplate, "The second argument should consist of integer numbers only")));
                        }

                        digitNotLessThan(count, 0, string.Format(wrongArgFormatExTemplate, "The second argument {0}"));

                        if (!double.TryParse(data[2], out double price))
                        {
                            exceptions.Add(new FormatException(string.Format(wrongArgFormatExTemplate, "The third argument should consist of numbers only")));
                        }

                        digitNotLessThan(price, 0, string.Format(wrongArgFormatExTemplate, "The third argument {0}"));

                        elementsCount += count;

                        if (elementsCount > MAX_ELEMENTS_RESTRICTION)
                        {
                            exceptions.Add(new FormatException(string.Format(wrongArgFormatExTemplate, $"Count of elements cannot be more than {MAX_ELEMENTS_RESTRICTION}")));
                        }

                        model.Blocks.Add(new Block(dataPointName, count, price));

                        if (_dataBlockCache.ContainsKey(dataPointName))
                        {
                            _dataBlockCache[dataPointName] += count;
                        }
                        else
                        {
                            _dataBlockCache.Add(dataPointName, count);
                        }

                        break;

                    case ROUTE_KEY_WORD:
                        checkBlockLength(ROUTE_BLOCK_ARGC, data.Length, currentBlock.Value.Value, wrongArgcExTemplate);

                        if (!double.TryParse(data[0], out double xCoor))
                        {
                            exceptions.Add(new FormatException(string.Format(wrongArgFormatExTemplate, "The \"X\" coordinate should be numeric")));
                        }

                        if (!double.TryParse(data[1], out double yCoor))
                        {
                            exceptions.Add(new FormatException(string.Format(wrongArgFormatExTemplate, "The \"Y\" coordinate should be numeric")));
                        }

                        if (!double.TryParse(data[2], out double pointPrice))
                        {
                            throw new FormatException(string.Format(wrongArgFormatExTemplate, "The price should be numeric"));
                        }

                        if ((model.Points.Count == 0) && (xCoor != 0) && (yCoor != 0))
                        {
                            exceptions.Add(new FormatException(string.Format(wrongArgFormatExTemplate, $"{ROUTE_KEY_WORD} block should starts with \"0 0\"")));
                        }

                        pointsCount++;

                        if (pointsCount > MAX_POINTS_RESTRICTION)
                        {
                            exceptions.Add(new FormatException(string.Format(wrongArgFormatExTemplate, $"Count of points cannot be more than {MAX_POINTS_RESTRICTION}")));
                        }

                        model.Points.Add(new Point(xCoor, yCoor, price: pointPrice));

                        break;

                    case ORDER_KEY_WORD:
                        checkBlockLength(ORDER_BLOCK_ARGC, data.Length, currentBlock.Value.Value, wrongArgcExTemplate);

                        var orderPointName = data[0];

                        checkPointName(orderPointName, wrongArgFormatExTemplate);

                        model.Order.Add(orderPointName);
                        _orderBlockCache.Add(lineNum, orderPointName);

                        break;

                    case TOP_KEY_WORD:
                        checkBlockLength(TOP_BLOCK_ARGC, data.Length, currentBlock.Value.Value, wrongArgcExTemplate);

                        if (!int.TryParse(data[0], out int firstBlock))
                        {
                            exceptions.Add(new FormatException(string.Format(wrongArgFormatExTemplate, "The first argument should consist of integer numbers only")));
                        }

                        digitNotLessThan(firstBlock, 0, string.Format(wrongArgFormatExTemplate, "The first argument {0}"));

                        if (!int.TryParse(data[1], out int secondBlock))
                        {
                            exceptions.Add(new FormatException(string.Format(wrongArgFormatExTemplate, $"The second argument should be numeric")));
                        }

                        digitNotLessThan(secondBlock, 0, string.Format(wrongArgFormatExTemplate, "The second argument {0}"));

                        if (!int.TryParse(data[2], out int direction))
                        {
                            exceptions.Add(new FormatException(string.Format(wrongArgFormatExTemplate, "The third argument should consist of integer numbers only")));
                        }

                        if (!_directions.Contains(direction))
                        {
                            exceptions.Add(new FormatException(string.Format(wrongArgFormatExTemplate, $"Direction should be one of ({string.Join(", ", _directions)})")));
                        }

                        var topology = new TopologyItem(firstBlock, secondBlock, direction);

                        model.Topology.Add(topology);
                        _topBlockCache.Add(lineNum, topology);

                        break;

                    // Этот код в теории должен быть недостижим
                    default:
                        exceptions.Add(new Exception(string.Format(exMessageTemplate, $"What a heck??? Line text: {line}")));
                        break;
                    }

                    isCurrentBlockNonEmpty = true;
                }

                string reachedBlockExMessage = string.Join(", ", blockReached.Where(block => !block.Value));

                if (!string.IsNullOrEmpty(reachedBlockExMessage))
                {
                    exceptions.Add(new FormatException($"Blocks missing: {reachedBlockExMessage}"));
                }

                // Создаём копию для работы
                var dataBlockCacheCopy = _dataBlockCache.ToDictionary(pair => pair.Key, pair => pair.Value);

                // Проверка блока ORDER на соответствие блока DATA
                foreach (var order in _orderBlockCache)
                {
                    var orderExTemplate = string.Format(EXCEPTION_TEMPLATE, order.Key, "{0}");

                    if (!dataBlockCacheCopy.ContainsKey(order.Value))
                    {
                        exceptions.Add(new FormatException(string.Format(orderExTemplate, $"{order.Value} element does not exists in {DATA_KEY_WORD} block")));
                    }

                    dataBlockCacheCopy[order.Value]--;

                    if (dataBlockCacheCopy[order.Value] < 0)
                    {
                        exceptions.Add(new FormatException(string.Format(orderExTemplate, $"Count of {order.Value} element cannot be more than specified in {DATA_KEY_WORD} block - {_dataBlockCache[order.Value]}")));
                    }
                }

                // Проверка блока TOP на соответствие блока DATA
                var topZeroCount = 0;
                foreach (var top in _topBlockCache)
                {
                    var routeExTemplate = string.Format(EXCEPTION_TEMPLATE, top.Key, "{0}");

                    if (top.Value.FirstBlock == 0 || top.Value.SecondBlock == 0)
                    {
                        topZeroCount++;

                        if (topZeroCount > REQUIRED_NUM_OF_ZEROS)
                        {
                            exceptions.Add(new FormatException(string.Format(routeExTemplate, $"Connection with zero element cannot be more than {REQUIRED_NUM_OF_ZEROS}")));
                        }
                    }
                    else
                    {
                        if (top.Value.FirstBlock > model.Order.Count)
                        {
                            exceptions.Add(new FormatException(string.Format(routeExTemplate, $"First arg element with {top.Value.FirstBlock} id does not exists in {ORDER_KEY_WORD} block")));
                        }

                        if (top.Value.SecondBlock > model.Order.Count)
                        {
                            exceptions.Add(new FormatException(string.Format(routeExTemplate, $"First arg element with {top.Value.SecondBlock} id does not exists in {ORDER_KEY_WORD} block")));
                        }

                        var blockName = model.Order[top.Value.SecondBlock - 1];

                        if (!_dataBlockCache.ContainsKey(blockName))
                        {
                            exceptions.Add(new FormatException(string.Format(routeExTemplate, $"{blockName} element does not exists in {DATA_KEY_WORD} block")));
                        }

                        _dataBlockCache[blockName]--;

                        if (_dataBlockCache[blockName] < 0)
                        {
                            exceptions.Add(new FormatException(string.Format(routeExTemplate, $"Count of {blockName} elements cannot be more than specified in {DATA_KEY_WORD} block - {_dataBlockCache[blockName]}")));
                        }
                    }
                }

                // Проверка, что все элементы из блока ORDER используются не больше двух (для Y - трёх) раз
                var orderElemetnsAvaliableConnections = model.Order.Select((item, index) => item.StartsWith("Y") ? 3 : 2).ToList();
                orderElemetnsAvaliableConnections.Insert(0, 2);
                foreach (var top in _topBlockCache)
                {
                    // Превышено допустимое количество использования блока
                    if (orderElemetnsAvaliableConnections[top.Value.FirstBlock]-- < 0)
                    {
                        exceptions.Add(new FormatException(string.Format(EXCEPTION_TEMPLATE, top.Key, $"Block №{top.Value.FirstBlock} usage exceeded")));
                    }
                    if (orderElemetnsAvaliableConnections[top.Value.SecondBlock]-- < 0)
                    {
                        exceptions.Add(new FormatException(string.Format(EXCEPTION_TEMPLATE, top.Key, $"Block №{top.Value.SecondBlock} usage exceeded")));
                    }
                }

                // Проверка, что все элементы из блока ORDER используются в блоке TOP
                var orderBlockKeys = _orderBlockCache.Keys.ToArray();
                for (int i = 0; i < model.Order.Count; i++)
                {
                    var orderLine  = orderBlockKeys[i];
                    var element    = _orderBlockCache[orderLine];
                    var usageCount = orderElemetnsAvaliableConnections[i + 1];

                    if (usageCount >= 2)
                    {
                        exceptions.Add(new FormatException(string.Format(EXCEPTION_TEMPLATE, orderLine, $"Element \"{element}\" (№{i + 1} in {ORDER_KEY_WORD} block) isn't used in {TOP_KEY_WORD} block")));
                    }
                    // У блока Y может быть 1 неиспользованное соединение, у остальных блоков - нет
                    else if (usageCount == 1 && !element.StartsWith("Y"))
                    {
                        exceptions.Add(new FormatException(string.Format(EXCEPTION_TEMPLATE, orderLine, $"Element \"{element}\" (№{i + 1} in {ORDER_KEY_WORD} block) has not enough connections. It used only {usageCount} time in {TOP_KEY_WORD} block")));
                    }
                }

                // Проверка количества соединений с нулевым элементом
                if (topZeroCount != REQUIRED_NUM_OF_ZEROS)
                {
                    exceptions.Add(new FormatException(string.Format(EXCEPTION_TEMPLATE, _topBlockCache.Keys.Last(), $"Connection with zero element should be equals to {REQUIRED_NUM_OF_ZEROS}")));
                }
            }
            catch (Exception exception)
            {
                exceptions.Add(exception);
            }
            finally
            {
                clearDicts();
            }

            return(new Tuple <Model, Exception[]>(model, exceptions.ToArray()));
        }