Richard Searle's Blog

Thoughts about software

Archive for September, 2010

Attempting to resolve the XPathFunction and Scala int problem

Posted by eggsearle on September 9, 2010

As previously noted, there are difficulties returning a Scala “primitive” type.

A view might provide a way to resolve that problem

class O[T,R<%Object](val f:Function1[T,R])  extends XPathFunction {  def evaluate(list:java.util.List[_]) = f(list.get(0).asInstanceOf[T])}
implicit def f1ToO[T,R<%Object](f:Function1[T,R]):O[T,R]=new O(f)
Map[String,XPathFunction]("a1"->{i:Int=>i*2})

Note the explicit return type on the f1ToO function, which is needed to avoid an error indicating that recursive functions need a return type!

Unfortunately, this approach fails because there are two implicit conversions in Predef.

This problem can be resolved by explicitly specifying the desired final type

class OInt[T,R<%java.lang.Integer](val f:Function1[T,R])  extends XPathFunction {  def evaluate(list:java.util.List[_]) = f(list.get(0).asInstanceOf[T])}
implicit def f1ToOInt[T,R<%java.lang.Integer](f:Function1[T,R]):OInt[T,R]=new OInt(f)

val mint= Map[String,XPathFunction]("int"->{i:Int=>i*2})
mint("int").evaluate(Collections.singletonList(12))

It is fortunate that only a few such mappings are needed to cover the impacted Scala types

Advertisements

Posted in Uncategorized | Leave a Comment »

Scala implicits to simplify XPathFunction

Posted by eggsearle on September 7, 2010

Custom functions for xpath expressions can be defined via XPathFunction. The standard Java code is verbose, with the explicit type casting of the arguments only increasing the code size.

An ideal implementation would allow the use of standard Scala functions, and provide an automagic conversion to XPathFunction required for integration with the Java XPath implementation.

class One[T,R<:Object](val f:Function1[T,R])  extends XPathFunction {
 def evaluate(list:java.util.List[_]) = f(list.get(0).asInstanceOf[T])
 }

Defines a class that wraps a Function1 and adapts it to the XPathFunction. Note that R must be a type that extends Object to conform to the XPathExpression.evaluate return type.

implicit def f1ToOne[T,R<:Object](f:Function1[T,R])=new One(f)

Provides an implicit conversion from Function1 to One (and hence to an implementation of XPathFunction)

Map[String,XPathFunction]("a1"->{i:String=>i*2})

Creates a simple naming from name to an XPathFunction, using anonymous Scala functions.

Additional adapter class, implicit conversion function pairs are then needed to cover the remaining FunctionN cases.

The Scala functions must still be written using Java types and in a manner that conforms to the expectations of XPathFunction.

This is illegal

Map[String,XPathFunction]("a1"->{i:Int=>i*2})

Since Int does not extend Object.

Map[String,XPathFunction]("a1"->{i:Integer=>i.intValue()*2})

Has the same return type and is still illegal.

But this is acceptable

Map[String,XPathFunction]("a1"->{i:Integer=>new Integer(i.intValue()*2)})

Posted in Scala | Leave a Comment »

The magic of Scala type inferencing

Posted by eggsearle on September 6, 2010

Considering mapping the Java XPathFunction into a standard Scala function represention.

An implemention for a single parameter might be

abstract class One[T,R<:Object] extends Function1[T,R] with XPathFunction { 
      def evaluate(list:java.util.List[_]) = apply(list.get(0).asInstanceOf[T]) 
}

With the identity function defined as

object OneString extends One[String,String]{ 
    def apply(s:String)=s
}

The repl has the following result

scala> OneString.evaluate(Collections.singletonList("ddd"))                                                                                                  
res22: String = ddd
scala> val xf:XPathFunction = OneString
xf: javax.xml.xpath.XPathFunction = <function1>
scala> xf.evaluate(Collections.singletonList("ddd"))       
res24: java.lang.Object = ddd

Note the types of the return values! The same function is being executed in each case, but via two aliases which have different associated types.

Posted in Scala | Leave a Comment »

A change in typing philosophy, indicated by Scala type classes

Posted by eggsearle on September 6, 2010

This discussion provides a clear and useful discussion of how Scala type classes might be used.

The code requires that a type be specified, which then indirectly defines the code to be executed

val i = foo[Int]("123") // 123
val f = foo[Float]("123.0") // 123.0

Where i and f will have types Int and Float respectively.

Coming from other strongly typed languages, one might be tempted to write

val i:Int = foo("123") // 123
val f:Float = foo("123.0") // 123.0

Which does not work. It might be argued that the type inferencer should be sophisticated enough to map this code to the earlier form.

Ignoring any technical reasons that prohibit the second form (or would lead to undesirable ambiguities), there are other reasons for prefer the first style.

  • The type is placed right next to the affected code, which is always helpful.
  • Passing the result of foo as an argument to a method is straightforward and well defined (even in the face of methods overloaded on argument type).
  • Conforms to the Scala style of only typing vals where absolutely necessary.
  • Conforms to the functional style which minimises temporary variables.

Posted in Scala | Leave a Comment »

How not to extend Scala XML

Posted by eggsearle on September 6, 2010

The first step in considering how to better integrate Scala and Java XML processing is a Google search to find the prior art.

That found the following gem. The scala.xml.Elem is prettyprinted to a String and then parsed into a DOM for processing by the Java xpath implementation.

This code illustrates a common concern with tools that hide the implementation details; they often hide significant performance problems. It might be argued that Scala implicit conversions will make this anti-pattern a common occurrence.

Posted in Scala | Leave a Comment »

Converting Scala XML into SAX events

Posted by eggsearle on September 2, 2010

This object generates SAX events from Scala XML to the provided ContentHandler.
If the target also implements LexicalHandler then comments are included.

import scala.xml._
import org.xml.sax.ext.LexicalHandler
import org.xml.sax._
import org.xml.sax.helpers._

import scala.xml.Utility._

object XMLDumper{

   def toXML(
    x: Node,
    target: ContentHandler,
    pscope: NamespaceBinding = TopScope)
  {
    val lexical = if(target.isInstanceOf[LexicalHandler]) target.asInstanceOf[LexicalHandler] else null

     target.startDocument

    x match {
      case c: Comment => if (lexical != null) lexical.comment(c.commentText.toCharArray,0,c.commentText.length)
      case a: Atom[_] =>  { val s = a text; target.characters(s.toCharArray,0,s.length) } 
      case e: EntityRef =>  { val s = e text; target.characters(s.toCharArray,0,s.length) } 
      case  p:ProcInstr => target.processingInstruction(p.target,p.proctext)
      case g: Group =>
        g.nodes foreach {toXML(_, target, x.scope)}
      case _  =>
        val uri = x.namespace
        val localName = x.label
        val qName = if(x.prefix == null) localName else x.prefix + ":" + localName

        val atts = new AttributesImpl() 
        if(x.attributes != null) toXML(x.attributes,atts)
        
        startPrefix(x.scope,pscope,target)
        target.startElement(uri,localName,qName,atts)  
          x.child foreach {toXML(_, target, x.scope)}
        target.endElement(uri,localName,qName) 
        endPrefix(x.scope,pscope,target)  
    }
    target.endDocument
  }

  private def startPrefix(ns:NamespaceBinding,stop:NamespaceBinding,target:ContentHandler){
     if(ns ne stop){
        target.startPrefixMapping(if(ns.prefix==null) "" else ns.prefix,ns.uri);
         startPrefix(ns.parent, stop,target)
     }
  }

  private def endPrefix(ns:NamespaceBinding,stop:NamespaceBinding,target:ContentHandler){
     if(ns ne stop){
       target.endPrefixMapping(if(ns.prefix==null) "" else ns.prefix);
       endPrefix(ns.parent, stop,target)
     }
  }

  private def toXML(att:MetaData,atts:AttributesImpl){
     att match  {
        case pa:PrefixedAttribute =>  atts.addAttribute(null,pa.key,pa.pre+":"+pa.key,"CDATA",toString(pa.value))
        case ua:UnprefixedAttribute =>  atts.addAttribute(null,ua.key,ua.key,"CDATA",toString(ua.value))
        case Null =>  //ignored
     }
     if(att hasNext)
       toXML(att.next,atts)
  }

  private def toString(ns:Seq[Node]) = {
     val sb = new StringBuilder
     ns foreach {_ match { 
          case a: Atom[_] => sb.append(a.text)
          case e:EntityRef => sb.append(e.text)
        }
      }
     sb.toString
  }

} 

With example usage

import java.io._
import javax.xml.parsers._
import javax.xml.transform._
import javax.xml.transform.sax._
import javax.xml.transform.stream._

object XMLDumperDriver extends Application{

val transformerFactory = (TransformerFactory.newInstance).asInstanceOf[SAXTransformerFactory]
val xformer = transformerFactory.newTransformerHandler
xformer.setResult(new StreamResult(System.out))

  val xml = <Outside> <Y xmlns="http://y"/> <X a="12" c="21" xmlns:h="http://com.com" xmlns:q="http://q"  >xxx &lt; <y xmlns:g="http://example.com"  h:b="13"/> </X> </Outside>

 XMLDumper.toXML(xml,xformer)
}

Posted in Scala | Leave a Comment »