Class/Object

eu.cdevreeze.yaidom.core

Scope

Related Docs: object Scope | package core

Permalink

final case class Scope(prefixNamespaceMap: Map[String, String]) extends Immutable with Product with Serializable

Scope mapping prefixes to namespace URIs, as well as holding an optional default namespace. In other words, in-scope namespaces.

The purpose of a eu.cdevreeze.yaidom.core.Scope is to resolve eu.cdevreeze.yaidom.core.QNames as eu.cdevreeze.yaidom.core.ENames.

For example, consider the following XML:

<book:Bookstore xmlns:book="http://bookstore/book">
  <book:Book ISBN="978-0321356680" Price="35" Edition="2">
    <book:Title>Effective Java (2nd Edition)</book:Title>
    <book:Authors>
      <auth:Author xmlns:auth="http://bookstore/author">
        <auth:First_Name>Joshua</auth:First_Name>
        <auth:Last_Name>Bloch</auth:Last_Name>
      </auth:Author>
    </book:Authors>
  </book:Book>
</book:Bookstore>

Then the (only) author element has the following scope:

Scope.from("book" -> "http://bookstore/book", "auth" -> "http://bookstore/author")

After all, the root element has the following scope:

Scope.Empty.resolve(Declarations.from("book" -> "http://bookstore/book"))

which is the same as:

Scope.from("book" -> "http://bookstore/book")

The (only) book element has no namespace declarations, so it has the same scope. That is also true for the authors element inside the book element. The (only) author element introduces a new namespace, and its scope is as follows:

Scope.from("book" -> "http://bookstore/book").resolve(Declarations.from("auth" -> "http://bookstore/author"))

which is indeed:

Scope.from("book" -> "http://bookstore/book", "auth" -> "http://bookstore/author")

The author element QName("auth:Author") has (optional) resolved name:

Scope.from("book" -> "http://bookstore/book", "auth" -> "http://bookstore/author").resolveQNameOption(QName("auth:Author"))

which is:

Some(EName("{http://bookstore/author}Author"))

A Scope must not contain prefix "xmlns" and must not contain namespace URI "http://www.w3.org/2000/xmlns/". Moreover, a Scope must not contain the XML namespace (prefix "xml", namespace URI "http://www.w3.org/XML/1998/namespace").

The Scope is backed by a map from prefixes (or the empty string for the default namespace) to (non-empty) namespace URIs.

This class depends on Declarations, but not the other way around.

Concise querying using QNames which are converted to ENames

EName-based querying is more robust than QName-based querying, but ENames are more verbose than QNames. We can get the best of both ENames and QNames by querying using (concise) QNames, and by using a Scope converting them to (stable) ENames.

For example:

val scope = Scope.from("xs" -> "http://www.w3.org/2001/XMLSchema")
import scope._

val elemDecls = schemaElem \\ withEName(QName("xs", "element").res)

This is exactly equivalent to the following query:

import HasENameApi._

val elemDecls = schemaElem \\ withEName("http://www.w3.org/2001/XMLSchema", "element")

Scope more formally

In order to get started using the class, this more formal section can safely be skipped. On the other hand, this section may provide a deeper understanding of the class.

Method resolve resolves a Declarations against this Scope, returning a new Scope. It could be defined by the following equality:

scope.resolve(declarations) == {
  val m = (scope.prefixNamespaceMap ++ declarations.withoutUndeclarations.prefixNamespaceMap) --
    declarations.retainingUndeclarations.prefixNamespaceMap.keySet
  Scope(m)
}

The actual implementation may be more efficient than that, but it is consistent with this definition.

Method relativize relativizes a Scope against this Scope, returning a Declarations. It could be defined by the following equality:

scope1.relativize(scope2) == {
  val declared = scope2.prefixNamespaceMap filter { case (pref, ns) => scope1.prefixNamespaceMap.getOrElse(pref, "") != ns }
  val undeclared = scope1.prefixNamespaceMap.keySet -- scope2.prefixNamespaceMap.keySet
  Declarations(declared) ++ Declarations.undeclaring(undeclared)
}

Again, the actual implementation may be more efficient than that, but it is consistent with this definition.

1. Property about two Scopes, and its proof

Methods relativize and resolve obey the following equality:

scope1.resolve(scope1.relativize(scope2)) == scope2

Below follows the proof. We distinguish among the following cases:

Prefix p can be the empty string, for the default namespace. For each of these cases, we prove that:

scope1.resolve(scope1.relativize(scope2)).prefixNamespaceMap.get(p) == scope2.prefixNamespaceMap.get(p)

Since there are no other cases, that would complete the proof.

If prefix p has the same mappings in both scopes, then:

scope1.relativize(scope2).prefixNamespaceMap.get(p).isEmpty

so the following equalities hold:

scope1.resolve(scope1.relativize(scope2)).prefixNamespaceMap(p)
scope1.prefixNamespaceMap(p)
scope2.prefixNamespaceMap(p)

so:

scope1.resolve(scope1.relativize(scope2)).prefixNamespaceMap.get(p) == scope2.prefixNamespaceMap.get(p)

If prefix p has different mappings in both scopes, then:

scope1.relativize(scope2).prefixNamespaceMap(p) == scope2.prefixNamespaceMap(p)
scope1.resolve(scope1.relativize(scope2)).prefixNamespaceMap(p) == scope2.prefixNamespaceMap(p)
scope1.resolve(scope1.relativize(scope2)).prefixNamespaceMap.get(p) == scope2.prefixNamespaceMap.get(p)

If prefix p only belongs to scope1, then:

scope1.relativize(scope2).prefixNamespaceMap(p) == "" // undeclaration
scope1.resolve(scope1.relativize(scope2)).prefixNamespaceMap.get(p).isEmpty
scope1.resolve(scope1.relativize(scope2)).prefixNamespaceMap.get(p) == scope2.prefixNamespaceMap.get(p) // both empty

if prefix p only belongs to scope2, then:

scope1.relativize(scope2).prefixNamespaceMap(p) == scope2.prefixNamespaceMap(p)
scope1.resolve(scope1.relativize(scope2)).prefixNamespaceMap(p) == scope2.prefixNamespaceMap(p)
scope1.resolve(scope1.relativize(scope2)).prefixNamespaceMap.get(p) == scope2.prefixNamespaceMap.get(p)

if prefix p belongs to neither scope, then obviously:

scope1.resolve(scope1.relativize(scope2)).prefixNamespaceMap.get(p).isEmpty
scope1.resolve(scope1.relativize(scope2)).prefixNamespaceMap.get(p) == scope2.prefixNamespaceMap.get(p) // both empty
2. Property about Scope and Declarations

Methods relativize and resolve also obey the following equality:

scope.relativize(scope.resolve(declarations)) == scope.minimize(declarations)

where scope.minimize(declarations) is defined by the following equality:

scope.minimize(declarations) == {
  val declared = declarations.withoutUndeclarations.prefixNamespaceMap filter { case (pref, ns) => scope.prefixNamespaceMap.getOrElse(pref, "") != ns }
  val undeclared = declarations.retainingUndeclarations.prefixNamespaceMap.keySet.intersect(scope.prefixNamespaceMap.keySet)
  Declarations(declared) ++ Declarations.undeclaring(undeclared)
}

It can be proven by distinguishing among the following cases:

Prefix p can be the empty string, for the default namespace. For each of these cases, it can be proven that:

scope.relativize(scope.resolve(declarations)).prefixNamespaceMap.get(p) == scope.minimize(declarations).prefixNamespaceMap.get(p)

Since there are no other cases, that would complete the proof. The proof itself is left as an exercise for the reader, as they say.

This and the preceding (proven) property are analogous to corresponding properties in the URI class.

Linear Supertypes
Serializable, Serializable, Product, Equals, Immutable, AnyRef, Any
Ordering
  1. Alphabetic
  2. By Inheritance
Inherited
  1. Scope
  2. Serializable
  3. Serializable
  4. Product
  5. Equals
  6. Immutable
  7. AnyRef
  8. Any
  1. Hide All
  2. Show All
Visibility
  1. Public
  2. All

Instance Constructors

  1. new Scope(prefixNamespaceMap: Map[String, String])

    Permalink

Type Members

  1. implicit class ToEName extends AnyRef

    Permalink

    Implicit class extending QNames with easy conversions to ENames, using this Scope.

Value Members

  1. final def !=(arg0: Any): Boolean

    Permalink
    Definition Classes
    AnyRef → Any
  2. final def ##(): Int

    Permalink
    Definition Classes
    AnyRef → Any
  3. def ++(scope: Scope): Scope

    Permalink

    Alias for append

  4. def --(prefixes: Set[String]): Scope

    Permalink

    Alias for minus

  5. final def ==(arg0: Any): Boolean

    Permalink
    Definition Classes
    AnyRef → Any
  6. def append(prefix: String, namespace: String): Scope

    Permalink

    Alias for append(Scope.from((prefix, namespace)))

  7. def append(scope: Scope): Scope

    Permalink

    Returns Scope(this.prefixNamespaceMap ++ scope.prefixNamespaceMap)

  8. final def asInstanceOf[T0]: T0

    Permalink
    Definition Classes
    Any
  9. def clone(): AnyRef

    Permalink
    Attributes
    protected[java.lang]
    Definition Classes
    AnyRef
    Annotations
    @throws( ... )
  10. def defaultNamespaceOption: Option[String]

    Permalink

    Returns the default namespace, if any, wrapped in an Option

  11. final def eq(arg0: AnyRef): Boolean

    Permalink
    Definition Classes
    AnyRef
  12. def filter(p: ((String, String)) ⇒ Boolean): Scope

    Permalink

    Returns Scope.from(this.prefixNamespaceMap.filter(p)).

  13. def filterKeys(p: (String) ⇒ Boolean): Scope

    Permalink

    Returns Scope.from(this.prefixNamespaceMap.filterKeys(p)).

  14. def filterNamespaces(p: (String) ⇒ Boolean): Scope

    Permalink

    Returns filter(kv => p(kv._2)).

  15. def finalize(): Unit

    Permalink
    Attributes
    protected[java.lang]
    Definition Classes
    AnyRef
    Annotations
    @throws( classOf[java.lang.Throwable] )
  16. final def getClass(): Class[_]

    Permalink
    Definition Classes
    AnyRef → Any
  17. def includingNamespace(namespaceUri: String, getFallbackPrefix: () ⇒ String): Scope

    Permalink

    Convenience method, returning the equivalent of:

    Convenience method, returning the equivalent of:

    this.resolve(
      Declarations.from(prefixForNamespace(namespaceUri, getFallbackPrefix) -> namespaceUri))

    If the namespace is "http://www.w3.org/XML/1998/namespace", this Scope is returned.

    If the fallback prefix is used and conflicts with an already used prefix (including ""), an exception is thrown, as documented for method prefixForNamespace.

    The following property holds:

    this.subScopeOf(this.includingNamespace(namespaceUri, getFallbackPrefix))
  18. def inverse: Map[String, Set[String]]

    Permalink

    Returns the inverse of this Scope, as Map from namespace URIs to collections of prefixes.

    Returns the inverse of this Scope, as Map from namespace URIs to collections of prefixes. These prefixes also include the empty String if this Scope has a default namespace.

  19. def isEmpty: Boolean

    Permalink

    Returns true if this Scope is empty.

    Returns true if this Scope is empty. Faster than comparing this Scope against the empty Scope.

  20. final def isInstanceOf[T0]: Boolean

    Permalink
    Definition Classes
    Any
  21. def isInvertible: Boolean

    Permalink

    Returns true if the inverse exists, that is, each namespace URI has a unique prefix (including the empty prefix for the default namespace, if applicable).

    Returns true if the inverse exists, that is, each namespace URI has a unique prefix (including the empty prefix for the default namespace, if applicable).

    In other words, returns true if the inverse of toMap is also a mathematical function, mapping namespace URIs to unique prefixes.

    Invertible scopes offer a one-to-one correspondence between QNames and ENames. This is needed, for example, for Paths. Only if there is such a one-to-one correspondence, the indexes in Paths and PathBuilders are stable, when converting between the two.

  22. def keySet: Set[String]

    Permalink

    Returns this.prefixNamespaceMap.keySet.

  23. def makeInvertible: Scope

    Permalink

    Returns an invertible Scope having the same namespaces but only one prefix per namespace.

    Returns an invertible Scope having the same namespaces but only one prefix per namespace. If this Scope has a default namespace, the returned Scope possibly has that default namespace as well.

  24. def minimize(declarations: Declarations): Declarations

    Permalink

    Returns the smallest sub-declarations decl of declarations such that this.resolve(decl) == this.resolve(declarations)

  25. def minus(prefixes: Set[String]): Scope

    Permalink

    Returns Scope(this.prefixNamespaceMap -- prefixes)

  26. def namespaces: Set[String]

    Permalink

    Returns this.prefixNamespaceMap.values.toSet.

    Returns this.prefixNamespaceMap.values.toSet. Hence, the "XML namespace" is not returned.

  27. final def ne(arg0: AnyRef): Boolean

    Permalink
    Definition Classes
    AnyRef
  28. def nonEmpty: Boolean

    Permalink

    Returns true if this Scope is not empty.

  29. final def notify(): Unit

    Permalink
    Definition Classes
    AnyRef
  30. final def notifyAll(): Unit

    Permalink
    Definition Classes
    AnyRef
  31. def prefixForNamespace(namespaceUri: String, getFallbackPrefix: () ⇒ String): String

    Permalink

    Returns one of the prefixes for the given namespace URI, if any, and otherwise falling back to a prefix (or exception) computed by the 2nd parameter.

    Returns one of the prefixes for the given namespace URI, if any, and otherwise falling back to a prefix (or exception) computed by the 2nd parameter. The result may be the empty string for the default namespace, if the default namespace is indeed equal to the passed namespace URI. The result is "xml" if the namespace URI is "http://www.w3.org/XML/1998/namespace".

    If the given namespace is the default namespace, but if there is also a non-empty prefix for the namespace, that non-empty prefix is returned. Otherwise, if the given namespace is the default namespace, the empty string is returned.

    The prefix fallback is only used if prefixesForNamespace(namespaceUri).isEmpty. If the fallback prefix conflicts with an already used prefix (including ""), an exception is thrown.

    This method can be handy when "inserting" an "element" into a parent tree, if one wants to reuse prefixes of the parent tree.

  32. val prefixNamespaceMap: Map[String, String]

    Permalink
  33. def prefixesForNamespace(namespaceUri: String): Set[String]

    Permalink

    Returns the prefixes for the given namespace URI.

    Returns the prefixes for the given namespace URI. The result includes the empty string for the default namespace, if the default namespace is indeed equal to the passed namespace URI. The result does not include "xml" for the implicit "xml" namespace (with namespace URI http://www.w3.org/XML/1998/namespace).

    The result is equivalent to:

    this.inverse.getOrElse(namespaceUri, Set())

    This method can be handy when "inserting" an "element" into a parent tree, if one wants to reuse prefixes of the parent tree.

  34. def relativize(scope: Scope): Declarations

    Permalink

    Relativizes the given Scope against this Scope, returning a Declarations object.

    Relativizes the given Scope against this Scope, returning a Declarations object.

    Inspired by java.net.URI, which has a similar method for URIs.

  35. def resolve(declarations: Declarations): Scope

    Permalink

    Resolves the given declarations against this Scope, returning an "updated" Scope.

    Resolves the given declarations against this Scope, returning an "updated" Scope.

    Inspired by java.net.URI, which has a similar method for URIs.

  36. def resolveQName(qname: QName)(implicit enameProvider: ENameProvider): EName

    Permalink

    Returns resolveQNameOption(qname)(enameProvider).get.

  37. def resolveQNameOption(qname: QName)(implicit enameProvider: ENameProvider): Option[EName]

    Permalink

    Tries to resolve the given QName against this Scope, returning None for prefixed names whose prefixes are unknown to this Scope.

    Tries to resolve the given QName against this Scope, returning None for prefixed names whose prefixes are unknown to this Scope.

    Note that the subScopeOf relation keeps the resolveQNameOption result the same, provided there is no default namespace. That is, if scope1.withoutDefaultNamespace.subScopeOf(scope2.withoutDefaultNamespace), then for each QName qname such that scope1.withoutDefaultNamespace.resolveQNameOption(qname).isDefined, we have:

    scope1.withoutDefaultNamespace.resolveQNameOption(qname) == scope2.withoutDefaultNamespace.resolveQNameOption(qname)
  38. def retainingDefaultNamespace: Scope

    Permalink

    Returns an adapted copy of this Scope, but retaining only the default namespace, if any

  39. def subScopeOf(scope: Scope): Boolean

    Permalink

    Returns true if this is a subscope of the given parameter Scope.

    Returns true if this is a subscope of the given parameter Scope. A Scope is considered subscope of itself.

  40. def superScopeOf(scope: Scope): Boolean

    Permalink

    Returns true if this is a superscope of the given parameter Scope.

    Returns true if this is a superscope of the given parameter Scope. A Scope is considered superscope of itself.

  41. final def synchronized[T0](arg0: ⇒ T0): T0

    Permalink
    Definition Classes
    AnyRef
  42. def toStringInXml: String

    Permalink

    Creates a String representation of this Scope, as it is shown in XML

  43. final def wait(): Unit

    Permalink
    Definition Classes
    AnyRef
    Annotations
    @throws( ... )
  44. final def wait(arg0: Long, arg1: Int): Unit

    Permalink
    Definition Classes
    AnyRef
    Annotations
    @throws( ... )
  45. final def wait(arg0: Long): Unit

    Permalink
    Definition Classes
    AnyRef
    Annotations
    @throws( ... )
  46. def withoutDefaultNamespace: Scope

    Permalink

    Returns an adapted copy of this Scope, but without the default namespace, if any

Inherited from Serializable

Inherited from Serializable

Inherited from Product

Inherited from Equals

Inherited from Immutable

Inherited from AnyRef

Inherited from Any

Ungrouped