Skip to content

Commit

Permalink
#56 #55 - onqname fix, refactor, prep M7 ~cut-release
Browse files Browse the repository at this point in the history
  • Loading branch information
chris-twiner committed May 10, 2024
1 parent 87b460a commit ed2816a
Show file tree
Hide file tree
Showing 12 changed files with 121 additions and 56 deletions.
2 changes: 1 addition & 1 deletion aalto/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<parent>
<artifactId>scales-xml-root_${scalaCompatVersion}</artifactId>
<groupId>org.scalesxml</groupId>
<version>0.6.0-M6</version>
<version>0.6.0-M7</version>
</parent>
<version>${project.parent.version}</version>
<packaging>jar</packaging>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import scales.utils.iteratee.IterateeFunctions
import scales.utils.iteratee.functions.IterOps
import scales.utils.iteratee.monadHelpers.{CanRunIt, Performer}
import scales.utils.trampolineIteratees._
import scales.xml.parser.pull.PullIteratees.onQNames

class AsyncPullTest extends junit.framework.TestCase {

Expand Down
6 changes: 3 additions & 3 deletions docs/Parsing_XML/RepeatedSections.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,9 @@ A simple, and recommended, way to leverage onDone is with the [foldOnDone functi
val i = text(head._2.get).toInt
// onQNames always returns the list as well as the XmlPath to allow matching against the input.
if (head._1 eq Headers) {
assertEquals(t._1, t._2)
// get new section
(i, 1)
assertEquals(t._1, t._2)
// get new section
(i, 1)
} else (t._1, i)
}
}
Expand Down
2 changes: 1 addition & 1 deletion jaxen/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<parent>
<artifactId>scales-xml-root_${scalaCompatVersion}</artifactId>
<groupId>org.scalesxml</groupId>
<version>0.6.0-M6</version>
<version>0.6.0-M7</version>
</parent>
<version>${project.parent.version}</version>
<packaging>jar</packaging>
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<artifactId>scales-xml-root_${scalaCompatVersion}</artifactId>

<packaging>pom</packaging>
<version>0.6.0-M6</version>
<version>0.6.0-M7</version>
<name>root</name>
<description>An alternate Scala Xml processing library</description>
<url>https://scalesxml.github.io/scales-xml_2.10</url>
Expand Down
2 changes: 1 addition & 1 deletion saxon-tests/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<parent>
<artifactId>scales-xml-root_${scalaCompatVersion}</artifactId>
<groupId>org.scalesxml</groupId>
<version>0.6.0-M6</version>
<version>0.6.0-M7</version>
</parent>
<version>${project.parent.version}</version>
<packaging>jar</packaging>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ object SaxonPresentation {
def foldNames {
val pull = pullXml(new java.io.
FileReader("./src/test/data/svnLogIteratorEg.xml"))
import idPullIteratees._

// Who touched what file at what revision
val LogEntries = List("log"l,"logentry"l)
Expand Down
2 changes: 1 addition & 1 deletion scales-xml/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<parent>
<artifactId>scales-xml-root_${scalaCompatVersion}</artifactId>
<groupId>org.scalesxml</groupId>
<version>0.6.0-M6</version>
<version>0.6.0-M7</version>
</parent>
<version>${project.parent.version}</version>
<packaging>jar</packaging>
Expand Down
111 changes: 84 additions & 27 deletions scales-xml/src/main/scala/scales/xml/parser/pull/PullIteratees.scala
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
package scales.xml.parser.pull

import scales.utils._
import scales.xml.{Elem, EndElem, PullType, QName, ScalesXml, XmlBuilder, XmlItem, XmlPath, addAndFocus, addChild, noXmlPath, parser, impl => ximpl}
import scales.xml.parser.strategies.{MemoryOptimisationStrategy, OptimisationToken}
import scales.xml.{Elem, EndElem, PullType, QName, ScalesXml, XmlBuilder, XmlItem, XmlPath, addAndFocus, addChild, noXmlPath, impl => ximpl}
import collection.FlatMapIterator
import scalaz.Free.Trampoline
import scalaz.Id.Id
import scalaz.Scalaz.ToEqualOps
import scalaz.effect.IO
import scalaz.{Equal, Monad}
import scalaz.iteratee.Input
import scalaz.iteratee.Input.{Empty, Eof}
import scalaz.iteratee.Iteratee.{iteratee, iterateeT}
import scalaz.iteratee.Iteratee.iterateeT
import scalaz.iteratee.{IterateeT, StepT}
import scalaz.iteratee.StepT.{Cont, Done}

Expand All @@ -20,7 +20,7 @@ class PullIterateeFunctions[F[_]](val F: Monad[F]){
import scales.xml.{QNamesMatch, PeekMatch}

def onQNames(qnames: List[QName])(implicit F: Monad[F]): ResumableIter[PullType, F, QNamesMatch] =
scales.xml.onQNames[F](qnames)
PullIteratees.onQNames[F](qnames)

/**
* Collects all data belonging to an element that matches
Expand All @@ -30,10 +30,10 @@ class PullIterateeFunctions[F[_]](val F: Monad[F]){
* as a path (each parent node containing only one child node).
*/
def onQNamesI(qnames: List[QName])(implicit qe: Equal[QName], F: Monad[F]): ResumableIter[PullType, F, QNamesMatch] =
scales.xml.onQNamesI[F](qnames)
PullIteratees.onQNamesI[F](qnames)

def skipv(downTo: Int*)(implicit F: Monad[F]): IterateeT[PullType, F, PeekMatch] =
scales.xml.skipv[F](downTo: _*)
PullIteratees.skipv[F](downTo: _*)

/**
* Skips all events until the indexes match downTo, can be seen as
Expand All @@ -44,7 +44,8 @@ class PullIterateeFunctions[F[_]](val F: Monad[F]){
* An empty list will simply return the first Element found.
*/
def skip(downTo: => List[Int])(implicit F: Monad[F]): IterateeT[PullType, F, PeekMatch] =
scales.xml.skip[F](downTo)
PullIteratees.skip[F](downTo)

}

/**
Expand All @@ -59,13 +60,27 @@ trait PullIteratees {
// not recommended but may help migrations
implicit val idPullIteratees = pullIterateesOf[Id]

// enumerators and iteratees follow
type PeekMatch = PullIteratees.PeekMatch

import scalaz.EphemeralStream
import scalaz.iteratee.{Iteratee, Enumerator, Input}
type QNamesMatch = PullIteratees.QNamesMatch

type QNamesMatch = (List[QName], Option[XmlPath])
/**
* Wraps XmlPull
*/
def iterate(path: List[QName], xml: XmlPull)(implicit qe: Equal[QName]): FlatMapIterator[XmlPath] = iterate(path, xml.it)

/**
* A wrapping around withIter(onDone(List(onQNames(path))))(enumXml(xml, _))
* it unwraps the data providing an Iterator[XPath]
*/
def iterate(path: List[QName], xml: Iterator[PullType])(implicit qe: Equal[QName]): FlatMapIterator[XmlPath] =
new Iterate(path, xml)

}

object PullIteratees {

type QNamesMatch = (List[QName], Option[XmlPath])
/**
* Collects all data belonging to an element that matches
* the list. <top><middle><ofInterest> content </ofInterest><ofInterest....
Expand All @@ -90,7 +105,6 @@ trait PullIteratees {
* The pairs allow the depth of each element to be followed. In particular this stops both descent and ascent problems in the
* pushing and popping on the stack. I.e. it covers the case where you have nested repeating QNames, both when you are looking for them
* and when your are not. Don't pop too early and don't incorrectly force a done.
*/
lazy val starter = Cont(step(Nil, (qnames.head, 0), qnames.tail.map((_, 0)), noXmlPath, false))
Expand Down Expand Up @@ -165,7 +179,62 @@ trait PullIteratees {
)
))
}
*/

lazy val starter = Cont(step(Nil, noXmlPath, false))

def step(before: List[QName], path: XmlPath, collecting: Boolean)(s: Input[PullType]): ResumableIter[PullType, F, QNamesMatch] = {
iterateeT( Monad[F].point(
s(el = {
case Left(elem@Elem(q, a, n)) => {
val npath = addAndFocus(path, elem)

val shouldCollect = collecting || (
before.size == (qnames.size - 1) &&
// eval only when needed
(before :+ q).zip(qnames).forall(p => p._1 === p._2)
)

Cont( step(before :+ q, npath, shouldCollect) )
}

case Left(x: XmlItem) =>
Cont(step(before,
if (collecting)
addChild(path, x)
else
path, collecting))

case Right(EndElem(q, n)) =>
// is this the end path ? We have to re-verify the path, do so only when needed but stops same height (but not same path)
// and nested repeat issues
val haveCollected = collecting && before.size == qnames.size && before.zip(qnames).forall(p => p._1 === p._2)
val npath =
if (haveCollected) {
// done with path
path
} else
if (collecting) {
// we are popping to the selected level
path.zipUp()
} else {
path.removeAndUp().getOrElse(noXmlPath)
}

if (haveCollected)
Done(((qnames, Some(npath)),
iterateeT( Monad[F].point( Cont(step(before.dropRight(1),
// remove all children on the next iteration
npath.removeAndUp.getOrElse(noXmlPath), collecting && !haveCollected))))), Empty[PullType])
else
Cont(step(before.dropRight(1), npath, collecting && !haveCollected))

},
empty = Cont(step(before, path, false)),
eof = Done(((qnames, None), iterateeT( Monad[F].point(starter) )), Eof[PullType])
)
))
}

if (qnames.isEmpty) error("Qnames is empty")

Expand Down Expand Up @@ -234,18 +303,6 @@ trait PullIteratees {
iterateeT( F.point( Cont(step(List[Int](), List(0), 1 :: downTo, noXmlPath)) ) )
}

/**
* Wraps XmlPull
*/
def iterate(path: List[QName], xml: XmlPull)(implicit qe: Equal[QName]): FlatMapIterator[XmlPath] = iterate(path, xml.it)

/**
* A wrapping around withIter(onDone(List(onQNames(path))))(enumXml(xml, _))
* it unwraps the data providing an Iterator[XPath]
*/
def iterate(path: List[QName], xml: Iterator[PullType])(implicit qe: Equal[QName]): FlatMapIterator[XmlPath] =
new Iterate(path, xml)

}

/**
Expand Down Expand Up @@ -297,9 +354,9 @@ class Iterate(path: List[QName], xml: Iterator[PullType])(implicit qe: Equal[QNa
proxies.beginSub(elem, XmlBuilder())

val shouldCollect = collecting || (
before.size == (path.size - 1) &&
before.size == (qnames.size - 1) &&
// eval only when needed
before.zip(path.dropRight(1)).forall(p => p._1 === p._2)
(before :+ q).zip(qnames.dropRight(1)).forall(p => p._1 === p._2)
)
set(before :+ q, proxies, shouldCollect)

Expand All @@ -313,7 +370,7 @@ class Iterate(path: List[QName], xml: Iterator[PullType])(implicit qe: Equal[QNa

// is this the end path ? We have to re-verify the path, do so only when needed but stops same height (but not same path)
// and nested repeat issues
val haveCollected = collecting && before.size == path.size && before.zip(path).forall(p => p._1 === p._2)
val haveCollected = collecting && before.size == qnames.size && before.zip(qnames).forall(p => p._1 === p._2)
if (haveCollected) {
res = proxies.proxyPath
} else
Expand Down
22 changes: 12 additions & 10 deletions scales-xml/src/test/scala/scales/xml/Presentation.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@ import scales.utils._
import ScalesUtils._
import scales.xml._
import ScalesXml._

import idIteratees._

import collection.path.Replace
import scales.xml.parser.pull.PullIteratees.onQNames

/**
* Code used in the intro presentation. Variables aren't re-used as the functions
Expand Down Expand Up @@ -179,12 +178,13 @@ object Presentation {
val LogEntries = List("log"l,"logentry"l)

// iterate is lazy be default
val bits = for{ entry : XmlPath <- iterate(LogEntries, pull)
revision <- entry.\.*@("revision"l).one
author <- entry.\*("author"l).one
path <- entry.\*("paths"l).\*("path"l)
kind <- path.\.*@("kind"l)
action <- path.\.*@("action"l)
val bits = for{
entry : XmlPath <- iterate(LogEntries, pull)
revision <- entry.\.*@("revision"l).one
author <- entry.\*("author"l).one
path <- entry.\*("paths"l).\*("path"l)
kind <- path.\.*@("kind"l)
action <- path.\.*@("action"l)
} yield (text(revision),
value(author), text(kind),
text(action), value(path))
Expand All @@ -204,8 +204,10 @@ object Presentation {

// combine the Iteratee
val ionDone = onDone(
List(onQNames(Authors),
onQNames(Paths))
List(
onQNames(Authors),
onQNames(Paths)
)
)

val allAuthorsAndFiles = foldOnDone(iteratorEnumerator(pull.it))(
Expand Down
3 changes: 2 additions & 1 deletion scales-xml/src/test/scala/scales/xml/PullIterateTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ class PullIterateTest extends junit.framework.TestCase {
(x, i) <- itr.zipWithIndex
} {
assertEquals("Wasn't giving back child", "{}child", qualifiedName(x))
assertEquals( "interesting content "+ (i+1) +"interesting content "+ (i + 2)
// bad added as #56 test case for onQName tests
assertEquals( "interesting content "+ (i+1) +"interesting content "+ (i + 2)+"interesting >>> BAAAD content "+(i+3)
, text(x))
val count = x.zipUp.children.size
if (count != 1){
Expand Down
Loading

0 comments on commit ed2816a

Please sign in to comment.