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

esl.parser.DefaultParser.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2017 Call Handling Services Ltd.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package esl.parser

import esl.domain._
import esl.util.StringHelpers

import scala.annotation.tailrec

object DefaultParser extends Parser {
  override def parse(text: String): (List[FSMessage], String) = {
    /**
      * This inner function does the split of incoming string using \n\n as delimiter.
      *
      * @param inpString is the input string to split
      * @param delim     \n\n is the delimiter
      * @param accum     this is the accumulator List to contain the partial output
      * @param depth     this is set to 3 now as the messages can have up to 3 section - header, header 1 and body
      * @return returns the list of string with their length so the calling function can start interpreting the structure
      */
    @tailrec
    def splitIncomingString(inpString: String, delim: String, accum: List[(String, Int)], depth: Int): List[(String, Int)] = {

      if (depth == 0) (inpString, inpString.length) :: accum
      else
        inpString.indexOf(delim) match {
          case -1 => (inpString, inpString.length) :: accum
          case x: Int =>
            val (left, right) = inpString.splitAt(x + 1)
            splitIncomingString(right, delim, (left, x + 1) :: accum, depth - 1)
        }
    }

    /**
      * This function gets the input string and sends it to splitIncomingString to get the list of strings first. It then
      * analyzes the list by iterating it and finding out the valid Event. Any string that is not able to interpret is
      * returned back
      *
      * @param textRemaining the string that has to be fitted in to Message objects
      * @return returns the tuple of 1. the string that cant be interpreted 2. The message that is properly parsed and a
      *         boolean to tell are there any string left to interpret.
      */
    def parseBasicMessage(textRemaining: String): (String, Option[BasicMessage], Boolean) = {
      val listOfThings = splitIncomingString(textRemaining, "\n\n", Nil, 3).reverse
      listOfThings match {
        case Nil => (textRemaining, None, true)
        case header :: Nil => (header._1, None, true)
        case header :: rest =>
          val hdr = StringHelpers.parseKeyValuePairs(header._1, ':')
          val contentLength = hdr.getOrElse("Content-Length", "-1").toInt

          if (contentLength != -1) {
            val foldedResult = rest.foldLeft(("", "", 0)) { (accum, elem) => {
              if (accum._3 == -1) (accum._1, accum._2 + elem._1, accum._3)
              else if (elem._2 + accum._3 >= contentLength) (accum._1 + elem._1, accum._2, -1)
              else (accum._1 + elem._1, accum._2, accum._3 + elem._2)
            }
            }
            if (foldedResult._3 == -1) {
              (foldedResult._2, Some(BasicMessage(hdr, Some(foldedResult._1.tail))), foldedResult._2.isEmpty)
            } else {
              (textRemaining, None, true)
            }
          } else {
            val curTextRemaining = textRemaining.substring(header._2)
            (curTextRemaining, Some(BasicMessage(hdr, None)), curTextRemaining.isEmpty)
          }
      }
    }

    /**
      * This function will convert event messages and also continue parsing BasicMessages if there is text still remaining
      *
      * @param doTxt This is the text left to process
      * @param msgs  This is the list of messages found in the text so far
      * @return Returns an Option of List of FreeSwitchMessage and any remaining data that could not be processed
      */
    @tailrec
    def doParse(doTxt: String, msgs: List[FSMessage]): (List[FSMessage], String) = {
      import scala.language.postfixOps
      val (textRemaining,basicMessage,isRemaining) = parseBasicMessage(doTxt)
      (textRemaining,basicMessage,isRemaining) match {
        case (t, None, f) if f =>
          if (msgs isEmpty) (Nil, t) else (msgs, t)
        case (t, None, f) if !f => doParse(t, msgs)
        case (t, Some(x), f) if !f && x.contentType == ContentTypes.eventPlain =>
          doParse(t, EventMessage(x) :: msgs)
        case (t, Some(x), f) if f && x.contentType == ContentTypes.eventPlain =>
          (EventMessage(x) :: msgs, t)
        case (t, Some(x), f) if !f && x.contentType == ContentTypes.commandReply &&
          x.headers.get(HeaderNames.eventName).contains(EventNames.ChannelData.name) =>
          doParse(t, CommandReply(x) :: msgs)
        case (t, Some(x), f) if !f && x.contentType == ContentTypes.commandReply &&
          !x.headers.contains(HeaderNames.eventName) =>
          doParse(t, CommandReply(x) :: msgs)
        case (t, Some(x), _) =>
          doParse(t, x :: msgs)
      }
    }

    doParse(text, List.empty)
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy