Skip to content
This repository was archived by the owner on Aug 17, 2019. It is now read-only.

Commit bfa9cec

Browse files
committed
Implement converter selection via strategies and provide one such strategy as the default
1 parent 63cb45a commit bfa9cec

11 files changed

+253
-108
lines changed

project/build.properties

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
project.organization=com.ckkloverdos
1919
project.name=converter
2020
sbt.version=0.7.7
21-
project.version=0.1.0
22-
build.scala.versions=2.9.1 2.8.2
21+
project.version=0.2.0
22+
build.scala.versions=2.9.1
2323
project.initialize=false
2424
publish.remote=false

project/build/Project.scala

+1-3
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,7 @@ class Project(info: ProjectInfo) extends DefaultProject(info) {
6161
val lib_slf4j = "org.slf4j" % "slf4j-api" % "1.6.1" % "compile" withSources()
6262
val lib_logback_simple = "ch.qos.logback" % "logback-classic" % "0.9.28" % "test" withSources()
6363
val lib_junit_interface = "com.novocode" % "junit-interface" % "0.7" % "test"
64-
val lib_maybe = "com.ckkloverdos" %% "maybe" % "0.1.0" % "compile" withSources()
65-
val lib_sysprop = "com.ckkloverdos" %% "sysprop" % "0.1.0" % "compile" withSources()
66-
val lib_typedkey = "com.ckkloverdos" %% "typedkey" % "0.1.0" % "compile" withSources()
64+
val lib_maybe = "com.ckkloverdos" %% "maybe" % "0.2.1" % "compile" withSources()
6765

6866
override def repositories =
6967
if (version.toString.endsWith("-SNAPSHOT")) super.repositories + ScalaToolsSnapshots

src/main/scala/com/ckkloverdos/convert/Converter.scala

+12-3
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,20 @@ import com.ckkloverdos.maybe.Maybe
2525
trait Converter[S, T] extends CanConvert {
2626
def sourceType: Manifest[S]
2727
def targetType: Manifest[T]
28+
def isStrictSource: Boolean
2829

29-
def convert(sourceValue: S): Maybe[T]
30-
def apply(sourceValue: S): Maybe[T] = convert(sourceValue)
30+
/**
31+
* Convert or throw an exception.
32+
*
33+
* This is a low-level function.
34+
*/
35+
@throws(classOf[ConverterException])
36+
def convertEx(sourceValue: S): T
37+
38+
def convert(sourceValue: S): Maybe[T] = Maybe(convertEx(sourceValue))
3139

32-
def toFunction: Function1[S, Maybe[T]] = (s) => convert(s)
40+
def apply(sourceValue: S): Maybe[T] = convert(sourceValue)
41+
def toFunction: S => Maybe[T] = (s) => convert(s)
3342
}
3443

3544
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright 2011 Christos KK Loverdos
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.ckkloverdos.convert
18+
19+
/**
20+
*
21+
* @author Christos KK Loverdos <loverdos@gmail.com>.
22+
*/
23+
final class ConverterException(cause: Throwable, msg: String, args: Any*) extends Exception(msg.format(args: _*)) {
24+
def this(msg: String, args: Any*) = this(null: Throwable, msg, args: _*)
25+
}
26+
27+
28+
/**
29+
*
30+
* @author Christos KK Loverdos <loverdos@gmail.com>.
31+
*/
32+
object ConverterException {
33+
def apply(msg: String, args: Any*): Nothing = throw new ConverterException(msg, args: _*)
34+
def apply(cause: Throwable, msg: String, args: Any*): Nothing = throw new ConverterException(msg, args: _*)
35+
}

src/main/scala/com/ckkloverdos/convert/ConverterImpl.scala

+4-6
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616

1717
package com.ckkloverdos.convert
1818

19-
import com.ckkloverdos.maybe.Maybe
20-
import com.ckkloverdos.maybe.Maybe._
2119
import Converter.{AnyManifest}
2220
/**
2321
*
@@ -26,12 +24,12 @@ import Converter.{AnyManifest}
2624
class ConverterImpl[S: Manifest, T: Manifest](
2725
val sourceType: Manifest[S],
2826
val targetType: Manifest[T],
29-
val strictSource: Boolean,
27+
val isStrictSource: Boolean,
3028
val function: S => T)
3129
extends Converter[S, T] {
3230

3331
def canConvertType(sm: AnyManifest, tm: AnyManifest) = {
34-
if(strictSource)
32+
if(isStrictSource)
3533
canConvertStrictSource(sm, tm)
3634
else
3735
canConvertNonStrictSource(sm, tm)
@@ -51,8 +49,8 @@ class ConverterImpl[S: Manifest, T: Manifest](
5149
sourceType.erasure.isAssignableFrom(from) && targetType.erasure.equals(to)
5250
}
5351

54-
def convert(sourceValue: S): Maybe[T] = Maybe(function(sourceValue))
52+
def convertEx(sourceValue: S) = function(sourceValue)
5553

5654
override def toString() =
57-
"Converter(" + List(sourceType, targetType, strictSource, function).mkString(",") + ")"
55+
"Converter(" + List(sourceType, targetType, isStrictSource, function).mkString(",") + ")"
5856
}

src/main/scala/com/ckkloverdos/convert/Converters.scala

+30-5
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,46 @@
1616

1717
package com.ckkloverdos.convert
1818

19-
import com.ckkloverdos.maybe.Maybe
2019
import Converter.AnyConverter
20+
import select.ConverterSelectionStrategy
21+
import com.ckkloverdos.maybe._
2122

2223
/**
2324
* An immutable registry for converters.
2425
*
2526
* @author Christos KK Loverdos <loverdos@gmail.com>.
2627
*/
27-
trait Converters extends CanConvert {
28-
def converters: Vector[AnyConverter]
28+
class Converters(selector: ConverterSelectionStrategy) extends CanConvert {
29+
def canConvertType(sm: Converter.AnyManifest, tm: Converter.AnyManifest) = {
30+
selector.find(sm, tm).isJust
31+
}
2932

30-
def converterFor[S: Manifest, T: Manifest](sm: Manifest[S], tm: Manifest[T], cacheResult: Boolean = true): Maybe[Converter[S, T]]
33+
def findConverter[S: Manifest, T: Manifest](sm: Manifest[S], tm: Manifest[T]): Maybe[Converter[S, T]] = {
34+
selector.find(sm, tm).asInstanceOf[Maybe[Converter[S, T]]]
35+
}
36+
37+
/**
38+
* Converts a value or throws an exception if the value cannot be converted.
39+
*/
40+
@throws(classOf[ConverterException])
41+
def convertValueEx[S: Manifest, T: Manifest](sourceValue: S, tm: Manifest[T]): T = {
42+
val sm = manifest[S]
43+
findConverter(sm, tm) match {
44+
case Just(cv) =>
45+
cv.convertEx(sourceValue)
46+
case NoVal =>
47+
ConverterException("Could not find converter from %s -> %s for value %s", sm, tm, sourceValue)
48+
case Failed(exception, explanation) =>
49+
ConverterException(exception, "Error [%s] trying to find converter from %s -> %s for value %s", explanation, sm, tm, sourceValue)
50+
}
51+
}
3152

3253
def convertValue[S: Manifest, T: Manifest](sourceValue: S, tm: Manifest[T]): Maybe[T] = {
33-
(for(converter <- converterFor(manifest[S], tm))
54+
(for(converter <- findConverter(manifest[S], tm))
3455
yield converter.convert(sourceValue)).flatten1
3556
}
57+
58+
def convertValueToInt[S: Manifest](sourceValue: S): Maybe[Int] = convertValue(sourceValue, Manifest.Int)
59+
def convertValueToLong[S: Manifest](sourceValue: S): Maybe[Long] = convertValue(sourceValue, Manifest.Long)
60+
def convertValueToBoolean[S: Manifest](sourceValue: S): Maybe[Boolean] = convertValue(sourceValue, Manifest.Boolean)
3661
}

src/main/scala/com/ckkloverdos/convert/ConvertersBuilder.scala

+7-2
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@
1717
package com.ckkloverdos.convert
1818

1919
import java.util.concurrent.locks.ReentrantLock
20-
import Converter.{AnyConverter, AnyManifest}
20+
import Converter.{AnyConverter}
2121
import ConverterHelpers.{lock}
2222
import org.slf4j.LoggerFactory
23+
import select.{ConverterSelectionStrategy, CachedMostSpecificTypeFirstSelection}
2324

2425
/**
2526
* A builder for converter registries.
@@ -66,5 +67,9 @@ class ConvertersBuilder {
6667
this
6768
}
6869

69-
def build: Converters = new ConvertersImpl(_converters)
70+
def build: Converters =
71+
buildWithStrategy(new CachedMostSpecificTypeFirstSelection(_))
72+
73+
def buildWithStrategy(f: (Traversable[AnyConverter]) => ConverterSelectionStrategy): Converters =
74+
new Converters(f(_converters))
7075
}

src/main/scala/com/ckkloverdos/convert/ConvertersImpl.scala

-83
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright 2011 Christos KK Loverdos
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.ckkloverdos.convert
18+
package select
19+
20+
import com.ckkloverdos.convert.Converter._
21+
import java.util.concurrent.locks.ReentrantLock
22+
import com.ckkloverdos.convert.ConverterHelpers._
23+
import com.ckkloverdos.maybe.{NoVal, Maybe, Just}
24+
25+
/**
26+
*
27+
* @author Christos KK Loverdos <loverdos@gmail.com>.
28+
*/
29+
final class CachedMostSpecificTypeFirstSelection(converters: Traversable[AnyConverter]) extends ConverterSelectionStrategy {
30+
private[this] val _lock = new ReentrantLock()
31+
private[this] val (_strictSourceConverters, _nonStrictSourceConverters) = converters.map(Just(_)).partition(_.get.isStrictSource)
32+
private[this] var _cache: Map[(AnyManifest, AnyManifest), Just[AnyConverter]] = Map()
33+
34+
def isCaching = true
35+
36+
def addToCache(sm: AnyManifest, tm: AnyManifest, cv: AnyConverter) = {
37+
lock(_lock) {
38+
_cache += ((sm, tm) -> Just(cv))
39+
}
40+
}
41+
42+
override def findCached(sm: Converter.AnyManifest, tm: Converter.AnyManifest) = {
43+
_cache.get((sm, tm)) match {
44+
case Some(jcv) =>
45+
jcv
46+
case None =>
47+
NoVal
48+
}
49+
}
50+
51+
def findNonCached(sm: AnyManifest, tm: AnyManifest): Maybe[AnyConverter] = {
52+
_strictSourceConverters.find(_.get.canConvertType(sm, tm)) match {
53+
case Some(jcv) =>
54+
jcv
55+
case None =>
56+
_nonStrictSourceConverters.find(_.get.canConvertType(sm, tm)) match {
57+
case Some(jcv) =>
58+
jcv
59+
case None =>
60+
NoVal
61+
}
62+
}
63+
}
64+
}

0 commit comments

Comments
 (0)