summaryrefslogtreecommitdiffstats
path: root/bundle-plugin/src/main/scala/com/yahoo/container/plugin/osgi/ExportPackageParser.scala
blob: 5cd93e84e87bb2283b60445d2171b7541f5d5c74 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.container.plugin.osgi

import scala.util.parsing.combinator.JavaTokenParsers
import ExportPackages.{Parameter, Export}
import com.yahoo.container.plugin.util.Extractors.ListOf
import scala.util.parsing.input.CharSequenceReader
import scala.annotation.tailrec

/**
 * @author  tonytv
 */
object ExportPackageParser extends JavaTokenParsers {
  val ListOfParameter = new ListOf(classOf[Parameter])


  def exportPackage = rep1sep(export, ",")

  //TODO: remove when fix is in current scala library
  //Fix for https://github.com/scala/scala-parser-combinators/pull/4
  def stringLiteral_fixed: Parser[String] = ("\""+"""([^"\p{Cntrl}\\]|\\[\\'"bfnrt]|\\u[a-fA-F0-9]{4})*+"""+"\"").r

  @SuppressWarnings(Array("unchecked"))
  def export : Parser[Export] = packageName ~ opt(";" ~> (parameters | export)) ^^ {
    case (packageName : String) ~ optional => {
      optional match {
        case None => Export(List(packageName.asInstanceOf[String]), List())
        case Some(e: Export) => e.copy(packageNames = packageName +: e.packageNames)
        case Some(ListOfParameter(parameters)) => Export(List(packageName), parameters)
      }
    }
  }

  def parameters = rep1sep(parameter, ";")

  def parameter = (directive | attribute) ^^ {
    case k ~ v => Parameter(k.toString, v.toString)
  }

  def directive = (extended_ <~ ":=") ~ argument
  def attribute = (extended_ <~ "=") ~ argument

  def packageName = rep1sep(ident_, ".") ^^ {
    x => x.mkString(".")
  }

  def extended = rep1("""\p{Alnum}""".r | "_" | "-" | ".") ^^ {
    _.mkString
  }

  def argument = (extended_ | stringLiteral_ | failure("argument expected")) ^^ {
    val quote = '"'.toString
    _.toString.stripPrefix(quote).stripSuffix(quote)
  }

  def parseAll(in: CharSequence): ParseResult[List[Export]] = {
    try {
      parseAll(exportPackage, in)
    } catch {
      case e: StackOverflowError =>
        throw new RuntimeException("Failed parsing Export-Package: '''\n" + in + "\n'''", e)
    }
  }

  //*** For debugging StackOverflow error **/
  def ident_ = printStackOverflow(ident)("ident")
  def stringLiteral_ = printStackOverflow(stringLiteral_fixed)("stringLiteral_fixed")
  def extended_ = printStackOverflow(extended)("extended")

  def printStackOverflow[T](p: => Parser[T])(name: String): Parser[T] = Parser{ in =>
    try {
      p(in)
    } catch {
      case e: StackOverflowError =>
        val input = in match {
          case reader: CharSequenceReader => readerToString(reader)
          case other => other.toString
        }
        println(s"***StackOverflow for $name with input '''$input'''")
        throw e
    }
  }

  @tailrec
  def readerToString(reader: CharSequenceReader, current: String = ""): String = {
    if (reader.atEnd) current
    else readerToString(reader.rest, current + reader.first)
  }
}