Microsoft TranslatorをScalaから使ってみた

「<a href="http://system.hateblo.jp/entry/2013/05/23/140550">Microsoft TranslatorをScalaから使ってみた</a>」から。
今回はソースそのまま（sbt用の設定は含まず）。

```scala:Translator.scala
import dispatch._, Defaults._
import scala.util.parsing.json.JSON
import java.util.Date
import scala.annotation.tailrec

 
object Translator {
  def main(args: Array[String]) {
    println("input English word")
    println("'!' to quit")

    @tailrec
    def readToTranslate(accessTokenMessage:AccessTokenMessage):Unit = {
      val line = Console.readLine("from: ")
      line match {
        case "!" => ()
        case word if word.trim.length > 0 => {
          val atm = AccessTokenMessage(Option(accessTokenMessage))
          println("to: " + translate(word.trim, atm))
          readToTranslate(atm)
        }
        case _ => {
          println(">> [empty]")
          readToTranslate(accessTokenMessage)
        }
      }
    }

    readToTranslate(AccessTokenMessage.empty)
  }

  def translate(word:String, atm:AccessTokenMessage):String = {
    MicrosoftTranslator.translate(word, atm.accessToken)
  }
}

object MicrosoftTranslator {
  def MsTransLatorPrimaryKey      = "Your Primary Key"
  def MsTransLatorClientId        = "Your Client Id"
  def MsTransLatorClientSecret    = "Your Client Secret"
  def MsTransLatorUrl             = "http://api.microsofttranslator.com/V2/Http.svc/Translate"
  def MsTransLatorScope           = "http://api.microsofttranslator.com"
  def MsTransLatorAccessTokenUrl  = "https://datamarket.accesscontrol.windows.net/v2/OAuth2-13"
  def MsTransLatorGrantType       = "client_credentials"

  // Dispatch http://dispatch.databinder.net/Dispatch.html
  // url(MsTransLatorAccessTokenUrl)は以下のようにも書ける。
  def authSecureHost = host("datamarket.accesscontrol.windows.net").secure
  def authSecureRequest = authSecureHost / "v2" / "OAuth2-13"

  // 新しいアクセストークンを取得する
  def newAccessTokenMessage: Map[String, String] = {
    val params = Map(
      "client_id"     -> MsTransLatorClientId,
      "client_secret" -> MsTransLatorClientSecret,
      "scope"         -> MsTransLatorScope,
      "grant_type"    -> MsTransLatorGrantType
      )
    val req = url(MsTransLatorAccessTokenUrl) << params
    val contents = Http(req OK as.String)
    val jsonResult = JSON.parseFull(contents())
    jsonResult.get.asInstanceOf [Map[String, String]]
  }

  def translate(word:String, accessToken:String):String = {
    val headerValue = "Bearer " + accessToken
    val headerParams = Map("Authorization" -> headerValue)
    val params = Map("text" -> word, "from" -> "en", "to" -> "ja")
    val req = url(MsTransLatorUrl) <<? params <:< headerParams
    val contents = Http(req OK as.xml.Elem)
    contents().text
  }
}

object AccessTokenMessage {
  def apply(message: Option[AccessTokenMessage]):AccessTokenMessage = message match {
    case Some(m) if !m.timeout => m
    case _ => new AccessTokenMessage(MicrosoftTranslator.newAccessTokenMessage)
  }

  def empty = null
}

class AccessTokenMessage(message:Map[String, String]) {
  val createTime = System.currentTimeMillis

  def accessToken = message("access_token").toString
  def expiresIn = message("expires_in").toLong

  def timeout =
    if (createTime + expiresIn * 1000 <= System.currentTimeMillis) true
    else false
}
```