src/Pure/library.scala
author wenzelm
Tue, 29 Nov 2011 21:29:53 +0100
changeset 46548 cd41e3903fbf
parent 46538 546d78f0d81f
child 46773 793bf5fa5fbf
permissions -rw-r--r--
separate compilation of PIDE vs. Pure sources, which enables independent Scala library;
     1 /*  Title:      Pure/library.scala
     2     Module:     PIDE
     3     Author:     Makarius
     4 
     5 Basic library.
     6 */
     7 
     8 package isabelle
     9 
    10 
    11 import java.lang.System
    12 import java.awt.Component
    13 import javax.swing.JOptionPane
    14 
    15 import scala.swing.ComboBox
    16 import scala.swing.event.SelectionChanged
    17 import scala.collection.mutable
    18 import scala.math.Ordering
    19 import scala.util.Sorting
    20 
    21 
    22 object Library
    23 {
    24   /* user errors */
    25 
    26   object ERROR
    27   {
    28     def apply(message: String): Throwable = new RuntimeException(message)
    29     def unapply(exn: Throwable): Option[String] =
    30       exn match {
    31         case e: RuntimeException => Some(Exn.message(e))
    32         case _ => None
    33       }
    34   }
    35 
    36   def error(message: String): Nothing = throw ERROR(message)
    37 
    38   def cat_error(msg1: String, msg2: String): Nothing =
    39     if (msg1 == "") error(msg1)
    40     else error(msg1 + "\n" + msg2)
    41 
    42 
    43   /* lists */
    44 
    45   def separate[A](s: A, list: List[A]): List[A] =
    46     list match {
    47       case x :: xs if !xs.isEmpty => x :: s :: separate(s, xs)
    48       case _ => list
    49     }
    50 
    51   def space_explode(sep: Char, str: String): List[String] =
    52     if (str.isEmpty) Nil
    53     else {
    54       val result = new mutable.ListBuffer[String]
    55       var start = 0
    56       var finished = false
    57       while (!finished) {
    58         val i = str.indexOf(sep, start)
    59         if (i == -1) { result += str.substring(start); finished = true }
    60         else { result += str.substring(start, i); start = i + 1 }
    61       }
    62       result.toList
    63     }
    64 
    65   def split_lines(str: String): List[String] = space_explode('\n', str)
    66 
    67   def sort_wrt[A](key: A => String, args: Seq[A]): List[A] =
    68   {
    69     val ordering: Ordering[A] =
    70       new Ordering[A] { def compare(x: A, y: A): Int = key(x) compare key(y) }
    71     val a = (new Array[Any](args.length)).asInstanceOf[Array[A]]
    72     for ((x, i) <- args.iterator zipWithIndex) a(i) = x
    73     Sorting.quickSort[A](a)(ordering)
    74     a.toList
    75   }
    76 
    77   def sort_strings(args: Seq[String]): List[String] = sort_wrt((x: String) => x, args)
    78 
    79 
    80   /* iterate over chunks (cf. space_explode) */
    81 
    82   def chunks(source: CharSequence, sep: Char = '\n') = new Iterator[CharSequence]
    83   {
    84     private val end = source.length
    85     private def next_chunk(i: Int): Option[(CharSequence, Int)] =
    86     {
    87       if (i < end) {
    88         var j = i; do j += 1 while (j < end && source.charAt(j) != sep)
    89         Some((source.subSequence(i + 1, j), j))
    90       }
    91       else None
    92     }
    93     private var state: Option[(CharSequence, Int)] = if (end == 0) None else next_chunk(-1)
    94 
    95     def hasNext(): Boolean = state.isDefined
    96     def next(): CharSequence =
    97       state match {
    98         case Some((s, i)) => { state = next_chunk(i); s }
    99         case None => Iterator.empty.next()
   100       }
   101   }
   102 
   103   def first_line(source: CharSequence): String =
   104   {
   105     val lines = chunks(source)
   106     if (lines.hasNext) lines.next.toString
   107     else ""
   108   }
   109 
   110 
   111   /* strings */
   112 
   113   def quote(s: String): String = "\"" + s + "\""
   114   def commas(ss: Iterable[String]): String = ss.iterator.mkString(", ")
   115   def commas_quote(ss: Iterable[String]): String = ss.iterator.mkString("\"", ", ", "\"")
   116 
   117 
   118   /* reverse CharSequence */
   119 
   120   class Reverse(text: CharSequence, start: Int, end: Int) extends CharSequence
   121   {
   122     require(0 <= start && start <= end && end <= text.length)
   123 
   124     def this(text: CharSequence) = this(text, 0, text.length)
   125 
   126     def length: Int = end - start
   127     def charAt(i: Int): Char = text.charAt(end - i - 1)
   128 
   129     def subSequence(i: Int, j: Int): CharSequence =
   130       if (0 <= i && i <= j && j <= length) new Reverse(text, end - j, end - i)
   131       else throw new IndexOutOfBoundsException
   132 
   133     override def toString: String =
   134     {
   135       val buf = new StringBuilder(length)
   136       for (i <- 0 until length)
   137         buf.append(charAt(i))
   138       buf.toString
   139     }
   140   }
   141 
   142 
   143   /* graph traversal */
   144 
   145   def topological_order[A](next: A => Iterable[A], starts: Iterable[A]): List[A] =
   146   {
   147     type Reached = (List[A], Set[A])
   148     def reach(reached: Reached, x: A): Reached =
   149     {
   150       val (rs, r_set) = reached
   151       if (r_set(x)) reached
   152       else {
   153         val (rs1, r_set1) = reachs((rs, r_set + x), next(x))
   154         (x :: rs1, r_set1)
   155       }
   156     }
   157     def reachs(reached: Reached, xs: Iterable[A]): Reached = (reached /: xs)(reach)
   158 
   159     reachs((Nil, Set.empty), starts)._1.reverse
   160   }
   161 
   162 
   163   /* simple dialogs */
   164 
   165   private def simple_dialog(kind: Int, default_title: String)
   166     (parent: Component, title: String, message: Any*)
   167   {
   168     Swing_Thread.now {
   169       val java_message = message map { case x: scala.swing.Component => x.peer case x => x }
   170       JOptionPane.showMessageDialog(parent,
   171         java_message.toArray.asInstanceOf[Array[AnyRef]],
   172         if (title == null) default_title else title, kind)
   173     }
   174   }
   175 
   176   def dialog = simple_dialog(JOptionPane.PLAIN_MESSAGE, null) _
   177   def warning_dialog = simple_dialog(JOptionPane.WARNING_MESSAGE, "Warning") _
   178   def error_dialog = simple_dialog(JOptionPane.ERROR_MESSAGE, "Error") _
   179 
   180   def confirm_dialog(parent: Component, title: String, option_type: Int, message: Any*): Int =
   181     Swing_Thread.now {
   182       val java_message = message map { case x: scala.swing.Component => x.peer case x => x }
   183       JOptionPane.showConfirmDialog(parent,
   184         java_message.toArray.asInstanceOf[Array[AnyRef]], title,
   185           option_type, JOptionPane.QUESTION_MESSAGE)
   186     }
   187 
   188 
   189   /* zoom box */
   190 
   191   class Zoom_Box(apply_factor: Int => Unit) extends ComboBox[String](
   192     List("50%", "70%", "85%", "100%", "125%", "150%", "175%", "200%", "300%", "400%"))
   193   {
   194     val Factor = "([0-9]+)%?"r
   195     def parse(text: String): Int =
   196       text match {
   197         case Factor(s) =>
   198           val i = Integer.parseInt(s)
   199           if (10 <= i && i <= 1000) i else 100
   200         case _ => 100
   201       }
   202     def print(i: Int): String = i.toString + "%"
   203 
   204     makeEditable()(c => new ComboBox.BuiltInEditor(c)(text => print(parse(text)), x => x))
   205     reactions += {
   206       case SelectionChanged(_) => apply_factor(parse(selection.item))
   207     }
   208     listenTo(selection)
   209     selection.index = 3
   210     prototypeDisplayValue = Some("00000%")
   211   }
   212 }
   213 
   214 
   215 class Basic_Library
   216 {
   217   val ERROR = Library.ERROR
   218   val error = Library.error _
   219   val cat_error = Library.cat_error _
   220 
   221   val space_explode = Library.space_explode _
   222   val split_lines = Library.split_lines _
   223 
   224   val quote = Library.quote _
   225   val commas = Library.commas _
   226   val commas_quote = Library.commas_quote _
   227 }