[from now] 2012/09/30 21:24:14

diff --git a/core/src/main/scala/webservice/response/ichiba/IchibaItemSearchResult.scala b/core/src/main/scala/webservice/response/ichiba/IchibaItemSearchResult.scala
index ee9708e..1d69069 100644
--- a/core/src/main/scala/webservice/response/ichiba/IchibaItemSearchResult.scala
+++ b/core/src/main/scala/webservice/response/ichiba/IchibaItemSearchResult.scala
@@ -28,6 +28,7 @@ class Item (
   ,val includePostage: Boolean
   ,val acceptCreditCard: Boolean
   ,val shopOfTheYear: Boolean
+  ,val shipOverseaStatus: ShipOverseaStatus
   ,val asurakuStatus: AsurakuStatus
   ,val affiliateRate: Double
   ,val startTime: Option[LocalDateTime]
@@ -124,6 +125,7 @@ object Item {
     includePostage <- tfVld(json.postageFlag, false, "postageFlag")
     acceptCreditCard <- tfVld(json.creditCardFlag, true, "creditCardFlag")
     shopOfTheYear <- tfVld(json.shopOfTheYearFlag, true, "shopOfTheYear")
+    shipOversea <- ShipOverseaStatus.parseVld(json.shipOverseasFlag, json.shipOverseasArea)
     asuraku <- AsurakuStatus.parseVld(json.asurakuFlag, json.asurakuClosingTime, json.asurakuArea)
     affiliateRate <- doubleVld(json.affiliateRate, "affiliateRate")
     startTime <- dateTimeOptVld(json.startTime, "startTime")
@@ -147,6 +149,7 @@ object Item {
     ,includePostage
     ,acceptCreditCard
     ,shopOfTheYear
+    ,shipOversea
     ,asuraku
     ,affiliateRate
     ,startTime
diff --git a/core/src/main/scala/webservice/response/ichiba/ShipOverseaStatus.scala b/core/src/main/scala/webservice/response/ichiba/ShipOverseaStatus.scala
new file mode 100644
index 0000000..7cc6012
--- /dev/null
+++ b/core/src/main/scala/webservice/response/ichiba/ShipOverseaStatus.scala
@@ -0,0 +1,30 @@
+package jp.co.rakuten.webservice
+
+import scalaz._, Scalaz._
+
+sealed trait ShipOverseaStatus
+case object DisableShipOversea extends ShipOverseaStatus
+
+case class AcceptShipOversea(areas: List[OverseaArea]) extends ShipOverseaStatus
+
+object ShipOverseaStatus {
+
+  def parseAreasVld(str: String): Validation[ApiError, List[OverseaArea]] = {
+    def error = JsonParseError("shipOverseasArea can't be parsed")
+    if (str endsWith "/")
+      error.failure
+    else
+      str
+        .split('/')
+        .toList
+        .map(OverseaArea.parseOpt)
+        .sequence[Option, OverseaArea]
+        .toSuccess(error)
+  }
+
+  def parseVld(flag: Int, areas: String): Validation[ApiError, ShipOverseaStatus] = flag match {
+    case 0 => DisableShipOversea.success
+    case 1 => parseAreasVld(areas) ∘ AcceptShipOversea.apply
+    case _ => JsonParseError("shipOverseasFlag must be in 0, 1").failure
+  }
+}
diff --git a/core/src/main/scala/webservice/types/OverseaArea.scala b/core/src/main/scala/webservice/types/OverseaArea.scala
index d0b3f8c..4a86576 100644
--- a/core/src/main/scala/webservice/types/OverseaArea.scala
+++ b/core/src/main/scala/webservice/types/OverseaArea.scala
@@ -5,7 +5,7 @@ sealed trait OverseaArea {
   def code: String
 }
 case object AllCountry extends OverseaArea {
-  val name = "ワールドワイド（すべての国）"
+  val name = "ワールドワイド"
   val code = "ALL"
 }
 case object UnitedStates extends OverseaArea {
@@ -149,3 +149,46 @@ case object VietNam extends OverseaArea {
   val code = "VN"
 }

+object OverseaArea {
+
+  def parseOpt: String => Option[OverseaArea] = {
+    case "ワールドワイド" => Some(AllCountry)
+    case "アメリカ" => Some(UnitedStates)
+    case "アルゼンチン" => Some(Argentina)
+    case "ブラジル" => Some(Brazil)
+    case "カナダ" => Some(Canada)
+    case "メキシコ" => Some(Mexico)
+    case "オーストリア" => Some(Austria)
+    case "ベルギー" => Some(Belgium)
+    case "デンマーク" => Some(Denmark)
+    case "フランス" => Some(France)
+    case "ドイツ" => Some(Germany)
+    case "ギリシャ" => Some(Greece)
+    case "イタリア" => Some(Italy)
+    case "モロッコ" => Some(Morocco)
+    case "オランダ" => Some(Netherlands)
+    case "ポーランド" => Some(Poland)
+    case "ポルトガル" => Some(Portugal)
+    case "ロシア" => Some(Russia)
+    case "スペイン" => Some(Spain)
+    case "スウェーデン" => Some(Sweden)
+    case "スイス" => Some(Switzerland)
+    case "トルコ" => Some(Turkey)
+    case "英国" => Some(UnitedKingdom)
+    case "オーストラリア" => Some(Australia)
+    case "中国" => Some(China)
+    case "香港" => Some(HongKong)
+    case "インド" => Some(India)
+    case "インドネシア" => Some(Indonesia)
+    case "韓国" => Some(Korea)
+    case "マレーシア" => Some(Malaysia)
+    case "ニュージーランド" => Some(NewZealand)
+    case "フィリピン" => Some(Philippines)
+    case "シンガポール" => Some(Singapore)
+    case "台湾" => Some(Taiwan)
+    case "タイ" => Some(Thailand)
+    case "ベトナム" => Some(VietNam)
+    case _ => None
+  }
+}
+
diff --git a/core/src/test/scala/webservice/response/ichiba/ItemSpec.scala b/core/src/test/scala/webservice/response/ichiba/ItemSpec.scala
index 77ddae7..5aa1e03 100644
--- a/core/src/test/scala/webservice/response/ichiba/ItemSpec.scala
+++ b/core/src/test/scala/webservice/response/ichiba/ItemSpec.scala
@@ -2,6 +2,7 @@ package jp.co.rakuten.webservice

 import org.specs2._, matcher.DataTables
 import scalaz._, Scalaz._
+import org.scala_tools.time.Imports._

 class ItemSpec extends Specification with DataTables { def is =

@@ -13,6 +14,24 @@ class ItemSpec extends Specification with DataTables { def is =
       "Doubleにparseできる場合はDouble値のsuccessを返す"                                            ! e2^
       "Doubleにparseできない場合はparamNameをmsgに含むJsonParseErrorのfailureを返す"                ! e3^
                                                                                                     p^
+    "longVld"                                                                                       ^
+      "Longにparseできる場合はLong値のsuccessを返す"                                                ! e4^
+      "Longにparseできない場合はparamNameをmsgに含むJsonParseErrorのfailureを返す"                  ! e5^
+                                                                                                    p^
+    "dateTimeOptVld"                                                                                ^
+      "空文字の場合はNoneをsuccessで返す"                                                           ! e6^
+      "yyyy-MM-dd hh:mm形式の有効な時刻をの場合、Some[LocalDateTime]をsuccessで返す"                ! e7^
+      ("フォーマットが不正な場合や、日時として不正な値の場合は" +
+        "paramNameをmsgに含むJsonParseErrorのfailureを返す")                                        ! e8^
+                                                                                                    p^
+    "tfVld"                                                                                         ^
+      "flagが1の場合はoneMeans、0の場合は!oneMeansがsuccessで返る"                                  ! e9^
+      "flagが0,1以外の場合はparamNameをmsgに含むJsonParseErrorのfailureを返す"                      ! e10^
+                                                                                                    p^
+//    "parseVld"                                                                                      ^
+//      "各パラメータが正常であればItemをsuccessで返す"                                               ! e11^
+//      "不正なパラメータを含む場合、JsonParseErrorのfailureを返す"                                   ! (e12_1 and e12_2 and e12_3)^
+//                                                                                                    p^
     "TODO"                                                                                          ^
       "Itemのテスト作ってから、実装をIchibaItemSearchResult.scalaから別ファイルに切り出す"          ! ee^
                                                                                                     end
@@ -29,7 +48,7 @@ class ItemSpec extends Specification with DataTables { def is =
     "input" || "result"      |
     "0.0"   !! 0.0.success   |
     "1.2"   !! 1.2.success   |
-    "-2.4"  !! -2.4.success   |
+    "-2.4"  !! -2.4.success  |
     "+4.7"  !! 4.7.success   |
     "123"   !! 123.0.success |> { (input, result) =>
       Item.doubleVld(input, "test") must equalTo(result)
@@ -47,6 +66,104 @@ class ItemSpec extends Specification with DataTables { def is =
       }
     }

+  def e4 =
+    "input" || "result"     |
+    "0"     !! 0L.success   |
+    "12"    !! 12L.success  |
+    "-24"   !! -24L.success |
+    "123"   !! 123L.success |> { (input, result) =>
+      Item.longVld(input, "test") must equalTo(result)
+    }
+
+  def e5 =
+    "input"  || "result"  |
+    ""       !! "failure" |
+    "abc"    !! "failure" |
+    "日本語" !! "failure" |
+    "a123"   !! "failure" |
+    "123a"   !! "failure" |> { (input, _) =>
+      Item.longVld(input, "test").toEither must beLeft.like {
+        case JsonParseError(msg) => msg must contain("test")
+      }
+    }
+
+  def e6 = Item.dateTimeOptVld("", "test").toEither must beRight.like {
+    case None => ok
+  }
+
+  def e7 =
+    "input"            || "result"                               |
+    "2012-09-30 00:00" !! new LocalDateTime(2012, 9, 30, 0, 0)   |
+    "2012-09-30 12:11" !! new LocalDateTime(2012, 9, 30, 12, 11) |
+    "2012-09-30 23:59" !! new LocalDateTime(2012, 9, 30, 23, 59) |
+    "2012-02-29 18:45" !! new LocalDateTime(2012, 2, 29, 18, 45) |> { (input, result) =>
+      Item.dateTimeOptVld(input, "test").toEither must beRight.like {
+        case Some(d) => d must equalTo(result)
+      }
+    }
+
+  def e8 =
+    "input"             || "result"  |
+    "2012-09-30 24:00"  !! "failure" |
+    "2012-9-30 00:00"   !! "failure" |
+    "a2012-09-30 12:11" !! "failure" |
+    "2012-09-30 23:59a" !! "failure" |
+    "日本語"            !! "failure" |
+    "2011-02-29 18:45"  !! "failure" |> { (input, _) =>
+      Item.dateTimeOptVld(input, "test").toEither must beLeft.like {
+        case JsonParseError(msg) => msg must contain("test")
+      }
+    }
+
+  def e9 =
+    "flag" | "oneMeans" | "result" |
+    1      ! true       ! true     |
+    1      ! false      ! false    |
+    0      ! true       ! false    |
+    0      ! false      ! true     |> { (flag, oneMeans, result) =>
+      Item.tfVld(flag, oneMeans, "test").toEither must beRight.like {
+        case b => b must equalTo(result)
+      }
+    }
+
+  def e10 =
+    "flag" | "oneMeans" |
+    -1     ! true       |
+    2      ! false      |
+    10     ! true       |
+    -99    ! false      |> { (flag, oneMeans) =>
+      Item.tfVld(flag, oneMeans, "test").toEither must beLeft.like {
+        case JsonParseError(msg) => msg must contain("test")
+      }
+    }
+
+//  def e11 = {
+//    val itemJson = new ItemJson (
+//       "itemName"
+//      ,"catchcopy"
+//      ,"itemCode"
+//      ,"1000"
+//      ,"itemCaption"
+//      ,"itemUrl"
+//      ,1
+//      ,0
+//      ,0
+//      ,1
+//      ,"shopName"
+//      ,"shopCode"
+//      ,"shopUrl"
+//      ,"123456"
+//      ,1
+//      ,List["small1", "small2"]
+//      ,List["medium1", "medium2"]
+//      ,"affiliateUrl"
+//      ,1
+//      ,1
+//      ,"東京都/神奈川県"
+//      ,
+//)
+//}
+
   def ee = ko
 }

diff --git a/core/src/test/scala/webservice/response/ichiba/ShipOverseaStatusSpec.scala b/core/src/test/scala/webservice/response/ichiba/ShipOverseaStatusSpec.scala
new file mode 100644
index 0000000..41edfa0
--- /dev/null
+++ b/core/src/test/scala/webservice/response/ichiba/ShipOverseaStatusSpec.scala
@@ -0,0 +1,72 @@
+package jp.co.rakuten.webservice
+
+import org.specs2._, matcher.DataTables
+import scalaz._, Scalaz._
+import org.scala_tools.time.Imports._
+
+class ShipOverseaStatusSpec extends Specification with DataTables { def is =
+
+  "ShipOverseaStatus"                                                                               ^
+    "parseAreasVld"                                                                                 ^
+      "「/」区切りの海外配送エリア文字列をparseし、結果をList[OverseaArea]のsuccessで返す"          ! e1^
+      ("空文字の場合や、「/」による分割後に不正な文字列が1つでもある場合" +
+         "、JsonParseErrorをfailureで返す")                                                         ! e2^
+                                                                                                    p^
+    "parseVld"                                                                                      ^
+      "flag = 0の場合はDisableShipOverseaをsuccessで返す"                                           ! e3^
+      "flag = 1で、areasのparseに成功した場合はAcceptShipOverseaをsuccessで返す"                    ! e4^
+      "flag = 1で、areasのparseに失敗した場合はJsonParseErrorをfailureで返す"                       ! e5^
+      "flagが0,1以外の場合はJsonParseErrorをfailureで返す"                                          ! e6^
+                                                                                                    end
+
+
+  def e1 =
+    "input"                                   || "result"                              |
+    "ワールドワイド"                          !! List(AllCountry)                      |
+    "英国"                                    !! List(UnitedKingdom)                   |
+    "アメリカ/アルゼンチン/ブラジル"          !! List(UnitedStates, Argentina, Brazil) |> { (input, result) =>
+      ShipOverseaStatus.parseAreasVld(input) must equalTo(result.success)
+    }
+
+  def e2 =
+    "input"                    || "result"  |
+    ""                         !! "failure" |
+    "/"                        !! "failure" |
+    "abc"                      !! "failure" |
+    "アメリカ/ダミー/ブラジル" !! "failure" |
+    "アルゼンチン/"            !! "failure" |
+    "/英国"                    !! "failure" |> { (input, result) =>
+      ShipOverseaStatus.parseAreasVld(input).toEither must beLeft.like {
+        case JsonParseError(_) => ok
+      }
+    }
+
+  def e3 = ShipOverseaStatus.parseVld(0, "ワールドワイド") must equalTo(DisableShipOversea.success)
+
+  def e4 =
+    "areaStr"        || "result"                                         |
+    "ワールドワイド" !! AcceptShipOversea(List(AllCountry))              |
+    "中国/香港/台湾" !! AcceptShipOversea(List(China, HongKong, Taiwan)) |> { (areaStr, result) =>
+        ShipOverseaStatus.parseVld(1, areaStr) must equalTo(result.success)
+    }
+
+  def e5 =
+    "areaStr" || "result"  |
+    ""        !! "failure" |
+    "abc"     !! "failure" |> { (areaStr, result) =>
+      ShipOverseaStatus.parseVld(1, areaStr).toEither must beLeft.like {
+        case JsonParseError(_) => ok
+      }
+    }
+
+  def e6 =
+    "flag" | "areaStr"        |
+    -1     ! "ワールドワイド" |
+    2      ! "アメリカ/韓国"  |
+    100    ! "dummy"          |
+    999    ! "dummy"          |> { (flag, areaStr) =>
+      ShipOverseaStatus.parseVld(flag, areaStr).toEither must beLeft.like {
+        case JsonParseError(_) => ok
+      }
+    }
+}