XML transformation / for comprehension question

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

XML transformation / for comprehension question

Clive Jevons-2
Hi List,

I'm using a transformation to multiply a given node from an XML  
template I load in from a file. The 'multiplier transform' is defined  
like this:

     val proTestMultiplier = new RuleTransformer(
         new RewriteRule () {
             override def transform (n: Node): Seq[Node] = n match {
                 case e @ <IDorKonfig /> if (e.attribute("name") ==  
"ProTest") =>
                     var dorList: Seq[Node] = Nil;
                     for (val count <- Iterator.range(0, 100)) {
                         dorList = dorList.concat(changeAttribute(
                             "name", "ProTest" + count.toString
()).transform(e));
                     }
                     dorList
                 case _ => n
             }
         }
     );


My question is whether I can do this with a proper comprehension -  
I've tried the following, but can't get the types to match up:

     val proTestMultiplier = new RuleTransformer(
         new RewriteRule () {
             override def transform (n: Node): Seq[Node] = n match {
                 case e @ <IDorKonfig /> if (e.attribute("name") ==  
"ProTest") =>
                     for (val count <- Iterator.range(0, 100)) yield
                         changeAttribute("name", "ProTest" +  
count.toString()).transform(e);
                 case _ => n
             }
         }
     );

The compiler then seems to complain that the result of the  
comprehension isn't coercible into Seq[Node] (at least, that's how I  
interpreted the error message ;)).

Any help / thoughts much appreciated :D

Here's my main and the changeAttribute def:

     def main (args: Array[String]): Unit = {
         var xml = XML.loadFile("eff_sample.xml");
         System.out.println(proTestMultiplier.transform(xml));
     }

     def changeAttribute (attName: String, newAttVal: String):  
RuleTransformer =
         new RuleTransformer(new RewriteRule () {
             val newAtt = new UnprefixedAttribute(attName, newAttVal,  
Null);
             def switchAttr (md: MetaData): MetaData = {
                 if (md == Null) md
                 else if (! (md.key == attName)) new  
UnprefixedAttribute(
                         md.key, md.value, switchAttr(md.next))
                 else new UnprefixedAttribute(
                     attName, newAttVal, switchAttr(md.next))
             }
             override def transform (n: Node) = n match {
                 case e @ Elem(p @ _ , l @ _, a @ _, s @ _, c @ _*)
                 if (e.attribute(attName) != null) =>
                     new Elem(p, l, switchAttr(a), s, c: _*)
                 case _ => n
             }
         });

The sample XML looks like this:

<?xml version="1.0" encoding="utf-8"?>

<import>

     <IDorKonfig name="ProTest"
         absoluteSchwelle="0.0"
         obereSchwelle="80.0"
         untereSchwelle="120.0"
         testArt="PROSPEKTIV" />

     <IDorKonfig name="RetroTest"
         absoluteSchwelle="0.0"
         obereSchwelle="80.0"
         untereSchwelle="125.0"
         testArt="RETROSPEKTIV" />

</import>


Cheers,
C
Reply | Threaded
Open this post in threaded view
|

Re: XML transformation / for comprehension question

Lex Spoon
Clive Jevons <[hidden email]> writes:

> My question is whether I can do this with a proper comprehension -
> I've tried the following, but can't get the types to match up:
>
>      val proTestMultiplier = new RuleTransformer(
>          new RewriteRule () {
>              override def transform (n: Node): Seq[Node] = n match {
>                  case e @ <IDorKonfig /> if (e.attribute("name") ==
> "ProTest") =>
>                      for (val count <- Iterator.range(0, 100)) yield
>                          changeAttribute("name", "ProTest" +
> count.toString()).transform(e);
>                  case _ => n
>              }
>          }
>      );

The idea is great, but there are two problems.

The first issue is that an Iterator is weaker than a full-blown
sequence and is not really enough.  So, use List.range instead of
Iterator.range .

(As an aside, an Interval class would seem nice for cases like
these....)

After that, you have an issue because transform(e) yields a *Seq* of
nodes, not a single node.  Thus, the for yields a list of lists of
nodes, not simply the list of nodes that you want from the function.
To deal with this, you could use List.flatten, but a more elegant
approach is to use a second clause of the for comprehension that
iterates of the answer from transform(e).

The final result looks like this:

  val proTestMultiplier2 = new RuleTransformer(
    new RewriteRule () {
      override def transform (n: Node): Seq[Node] = n match {
        case e @ <IDorKonfig /> if (e.attribute("name") ==
          "ProTest") =>
              for (val count <- List.range(0, 100);
                   val node <-
                       changeAttribute("name", "ProTest" +
                                       count.toString()).transform(e).toList)
                yield node
        case _ => n
      }
    }
  );



This is a good example of a place to use for comprehensions -- good intuition!

-Lex