Oto ona. Ponieważ NamespaceBinding jest zagnieżdżony (każdy ns ma element nadrzędny, z wyjątkiem TopScope), musimy się wycofać, aby to naprawić. Ponadto każdy ns ma identyfikator URI i prefiks, a my musimy zmienić oba.
Poniższa funkcja zmieni tylko jeden konkretny identyfikator URI i prefiks, a także sprawdzi wszystkie przestrzenie nazw, aby sprawdzić, czy zmiany wymagają prefiks lub URI. Zmieni to przedrostek lub URI niezależnie od siebie, co może nie być tym, co jest potrzebne. Ale to nie jest wielka sprawa.
Co do reszty, wystarczy dopasować wzorzec Elem do rekurencji w każdej części kodu XML. Ach, tak, zmienia również prefiks elementów. Ponownie, jeśli nie jest to pożądane, łatwo je zmienić.
Kod zakłada, że nie ma potrzeby powtarzania się w "innych" częściach XML - reszta będzie zwykle tekstem. Zakłada też, że nie ma gdzie indziej przestrzeni nazw. Nie jestem ekspertem od XML, więc mógłbym się mylić w obu przypadkach. Po raz kolejny zmiana powinna być łatwa - wystarczy wykonać wzór.
def changeNS(el: Elem,
oldURI: String, newURI: String,
oldPrefix: String, newPrefix: String): Elem = {
def replace(what: String, before: String, after: String): String =
if (what == before) after else what
def fixScope(ns: NamespaceBinding): NamespaceBinding =
if(ns == TopScope)
TopScope
else new NamespaceBinding(replace(ns.prefix, oldPrefix, newPrefix),
replace(ns.uri, oldURI, newURI),
fixScope(ns.parent))
def fixSeq(ns: Seq[Node]): Seq[Node] = for(node <- ns) yield node match {
case Elem(prefix, label, attribs, scope, children @ _*) =>
Elem(replace(prefix, oldPrefix, newPrefix),
label,
attribs,
fixScope(scope),
fixSeq(children) : _*)
case other => other
}
fixSeq(el.theSeq)(0).asInstanceOf[Elem]
}
Daje to nieoczekiwany wynik. Zakres jest dodawany do wszystkich elementów. Dzieje się tak, ponieważ NamespaceBinding nie definiuje metody równości, a więc używa równości odniesienia. Otworzyłem dla niego bilet, 2138, który został już zamknięty, więc Scala 2.8 nie będzie miała tego problemu.
Tymczasem poniższy kod będzie działał poprawnie. Zachowuje pamięć podręczną obszarów nazw. Rozkłada on również NamespaceBinding na listę przed jej użyciem.
def changeNS(el: Elem,
oldURI: String, newURI: String,
oldPrefix: String, newPrefix: String): Elem = {
val namespaces = scala.collection.mutable.Map.empty[List[(String, String)],NamespaceBinding]
def replace(what: String, before: String, after: String): String =
if (what == before) after else what
def unfoldNS(ns: NamespaceBinding): List[(String, String)] = ns match {
case TopScope => Nil
case _ => (ns.prefix, ns.uri) :: unfoldNS(ns.parent)
}
def foldNS(unfoldedNS: List[(String, String)]): NamespaceBinding = unfoldedNS match {
case knownNS if namespaces.isDefinedAt(knownNS) => namespaces(knownNS)
case (prefix, uri) :: tail =>
val newNS = new NamespaceBinding(prefix, uri, foldNS(tail))
namespaces(unfoldedNS) = newNS
newNS
case Nil => TopScope
}
def fixScope(ns: NamespaceBinding): NamespaceBinding =
if(ns == TopScope)
ns
else {
val unfoldedNS = unfoldNS(ns)
val fixedNS = for((prefix, uri) <- unfoldedNS)
yield (replace(prefix, oldPrefix, newPrefix), replace(uri, oldURI, newURI))
if(!namespaces.isDefinedAt(unfoldedNS))
namespaces(unfoldedNS) = ns // Save for future use
if(fixedNS == unfoldedNS)
ns
else
foldNS(fixedNS)
}
def fixSeq(ns: Seq[Node]): Seq[Node] = for(node <- ns) yield node match {
case Elem(prefix, label, attribs, scope, children @ _*) =>
Elem(replace(prefix, oldPrefix, newPrefix),
label,
attribs,
fixScope(scope),
fixSeq(children) : _*)
case other => other
}
fixSeq(el.theSeq)(0).asInstanceOf[Elem]
}