WIP: IMEventModel end-to-end chain
[aquarium] / src / main / scala / gr / grnet / aquarium / util / package.scala
1 /*
2  * Copyright 2011-2012 GRNET S.A. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or
5  * without modification, are permitted provided that the following
6  * conditions are met:
7  *
8  *   1. Redistributions of source code must retain the above
9  *      copyright notice, this list of conditions and the following
10  *      disclaimer.
11  *
12  *   2. Redistributions in binary form must reproduce the above
13  *      copyright notice, this list of conditions and the following
14  *      disclaimer in the documentation and/or other materials
15  *      provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
18  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
21  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
24  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
25  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  *
30  * The views and conclusions contained in the software and
31  * documentation are those of the authors and should not be
32  * interpreted as representing official policies, either expressed
33  * or implied, of GRNET S.A.
34  */
35
36 package gr.grnet.aquarium
37
38 import com.ckkloverdos.maybe.{Failed, MaybeOption, Just, NoVal, Maybe}
39 import java.nio.charset.Charset
40 import java.io.{PrintWriter, StringWriter}
41 import annotation.tailrec
42
43
44 /**
45  * Utility functions.
46  *
47  * @author Christos KK Loverdos <loverdos@gmail.com>.
48  */
49 package object util {
50   final val UTF_8_Charset = Charset.forName("UTF-8")
51
52   def tryOption[A](f: => A): Option[A] = {
53     try Some(f)
54     catch {
55       case _: Exception => None
56     }
57   }
58
59   def withCloseable[C <: { def close(): Unit}, A](c: C)(f: C => A): A = {
60     try {
61       f(c)
62     } finally {
63       c.close()
64     }
65   }
66
67   /**
68    * Compute the class name excluding any leading packages.
69    *
70    * This is basically the name after the last dot.
71    */
72   def shortNameOfClass(theClass: Class[_]): String = {
73     val cname = theClass.getName
74     cname.substring(cname.lastIndexOf(".") + 1)
75   }
76
77   /**
78    * For the class of the provided object, compute the class name excluding any leading packages.
79    *
80    * This is basically the name after the last dot.
81    *
82    * The `null` value is mapped to string `"null"`.
83    */
84   def shortClassNameOf(anyRef: AnyRef): String = {
85     anyRef match {
86       case null =>
87         "<null>"
88       case clz: Class[_] =>
89         shortNameOfClass(clz)
90       case obj =>
91         shortNameOfClass(obj.getClass)
92     }
93   }
94
95   def safeToStringOrNull(obj: AnyRef): String = obj match {
96     case null => null
97     case _ => obj.toString
98   }
99
100   def displayableObjectInfo(obj: AnyRef): String = {
101     "[%s] %s".format(obj.getClass, obj)
102   }
103
104   /**
105    * This basically turns an [[scala.Option]] into a [[com.ckkloverdos.maybe.Maybe]] when asking a
106    * [[scala.collection.Map]] for a key.
107    *
108    * @param map The input map.
109    * @param key The key we are interested in.
110    * @tparam A The type of keys.
111    * @tparam B The type of values.
112    *
113    * @return A [[com.ckkloverdos.maybe.Just]] if a value was found, a
114    *           [[com.ckkloverdos.maybe.NoVal]] if nothing was found and a
115    *           [[com.ckkloverdos.maybe.Failed]] if some error happened.
116    */
117   @inline
118   def findFromMapAsMaybe[A, B <: AnyRef](map: scala.collection.Map[A, B], key: A): Maybe[B] = Maybe {
119     map.get(key) match {
120       case Some(value) ⇒
121         value
122       case None ⇒
123         null.asInstanceOf[B]
124     }
125   }
126
127   @inline
128   def findAndRemoveFromMap[A, B <: AnyRef](map: scala.collection.mutable.Map[A, B], key: A): Maybe[B] = Maybe {
129     map.get(key) match {
130       case Some(value) ⇒
131         map -= key
132         value
133       case None ⇒
134         null.asInstanceOf[B]
135     }
136   }
137
138   // Dear scalac. Optimize this.
139   def nspaces(n: Int): String = {
140     ("" /: (1 to n)) ((string, _) => string + " ")
141   }
142
143   def rpad(s: String, size: Int) = {
144     s + nspaces((size - s.length()) max 0)
145   }
146   
147   def maxStringSize[A](trav: Traversable[A]): Int = {
148     (0 /: trav)(_ max _.toString.length)
149   }
150
151   /**
152    * Given a [[com.ckkloverdos.maybe.Maybe]] that is actually a [[com.ckkloverdos.maybe.Failed]], return the latter.
153    *
154    * Use this only when you are sure what the `maybe` contains, since the methods can break type safety.
155    *
156    * @param maybe
157    * @tparam A
158    * @return
159    */
160   def failedForSure[A](maybe: Maybe[A]): Failed = {
161     maybe.asInstanceOf[Failed]
162   }
163
164   /**
165    * Given a [[com.ckkloverdos.maybe.Maybe]] that is actually a [[com.ckkloverdos.maybe.Just]], return the latter.
166    *
167    * Use this only when you are sure what the `maybe` contains, since the methods can break type safety.
168    *
169    * @param maybe
170    * @tparam A
171    * @return
172    */
173   def justForSure[A](maybe: Maybe[A]): Just[A] = {
174     maybe.asInstanceOf[Just[A]]
175   }
176
177   /**
178    * Transform an array of bytes to a string, assuming UTF-8 encoding.
179    */
180   def makeString(bytes: Array[Byte]): String = {
181     new String(bytes, UTF_8_Charset)
182   }
183
184   /**
185    * Transform a string to an array of bytes, following a UTF-8 decoding scheme.
186    */
187   def makeBytes(string: String): Array[Byte] = {
188     string.getBytes(UTF_8_Charset)
189   }
190
191   /**
192    * Return the stack trace of the given [[java.lang.Throwable]] in the form of [[java.lang.String]].
193    *
194    * @param t
195    * @return
196    */
197   def stringOfStackTrace(t: Throwable): String = {
198     val sw = new StringWriter()
199     val pw = new PrintWriter(sw)
200
201     t.printStackTrace(pw)
202     pw.flush()
203     pw.close()
204
205     sw.toString
206   }
207
208
209   def chainOfCauses(t: Throwable): List[String] = {
210     import scala.collection.mutable.ListBuffer
211     @tailrec
212     def loop(t: Throwable, buffer: ListBuffer[String]): List[String] = {
213       t match {
214         case null ⇒
215           buffer.toList
216
217         case _ ⇒
218           buffer.append("%s(%s)".format(shortClassNameOf(t), t.getMessage))
219           loop(t.getCause, buffer)
220       }
221     }
222
223     loop(t, new ListBuffer[String])
224   }
225 }