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
has the same mappings inscope1
andscope2
- Prefix
p
has different mappings inscope1
andscope2
- Prefix
p
only belongs toscope1
- Prefix
p
only belongs toscope2
- Prefix
p
belongs to neither scope
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
has the same mappings inscope
anddeclarations
(so no undeclaration) - Prefix
p
has different mappings inscope
anddeclarations
(but no undeclaration) - Prefix
p
belongs toscope
and is undeclared indeclarations
- Prefix
p
only belongs toscope
, and does not occur indeclarations
- Prefix
p
only occurs indeclarations
, without being undeclared, and does not occur inscope
- Prefix
p
only occurs indeclarations
, in an undeclaration, and does not occur inscope
- Prefix
p
neither occurs inscope
nor indeclarations
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.
- Alphabetic
- By Inheritance
- Scope
- Serializable
- Serializable
- Product
- Equals
- Immutable
- AnyRef
- Any
- Hide All
- Show All
- Public
- All
Instance Constructors
- new Scope(prefixNamespaceMap: Map[String, String])
Type Members
Value Members
-
final
def
!=(arg0: Any): Boolean
- Definition Classes
- AnyRef → Any
-
final
def
##(): Int
- Definition Classes
- AnyRef → Any
-
def
++(scope: Scope): Scope
Alias for
append
-
def
--(prefixes: Set[String]): Scope
Alias for
minus
-
final
def
==(arg0: Any): Boolean
- Definition Classes
- AnyRef → Any
-
def
append(prefix: String, namespace: String): Scope
Alias for
append(Scope.from((prefix, namespace)))
-
def
append(scope: Scope): Scope
Returns
Scope(this.prefixNamespaceMap ++ scope.prefixNamespaceMap)
-
final
def
asInstanceOf[T0]: T0
- Definition Classes
- Any
-
def
clone(): AnyRef
- Attributes
- protected[java.lang]
- Definition Classes
- AnyRef
- Annotations
- @throws( ... )
-
def
defaultNamespaceOption: Option[String]
Returns the default namespace, if any, wrapped in an Option
-
final
def
eq(arg0: AnyRef): Boolean
- Definition Classes
- AnyRef
-
def
filter(p: ((String, String)) ⇒ Boolean): Scope
Returns
Scope.from(this.prefixNamespaceMap.filter(p))
. -
def
filterKeys(p: (String) ⇒ Boolean): Scope
Returns
Scope.from(this.prefixNamespaceMap.filterKeys(p))
. -
def
finalize(): Unit
- Attributes
- protected[java.lang]
- Definition Classes
- AnyRef
- Annotations
- @throws( classOf[java.lang.Throwable] )
-
final
def
getClass(): Class[_]
- Definition Classes
- AnyRef → Any
-
def
includingNamespace(namespaceUri: String, getFallbackPrefix: () ⇒ String): Scope
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))
-
def
inverse: Map[String, Set[String]]
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.
-
def
isEmpty: Boolean
Returns true if this Scope is empty.
Returns true if this Scope is empty. Faster than comparing this Scope against the empty Scope.
-
final
def
isInstanceOf[T0]: Boolean
- Definition Classes
- Any
-
def
isInvertible: Boolean
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
Path
s. Only if there is such a one-to-one correspondence, the indexes inPath
s andPathBuilder
s are stable, when converting between the two. -
def
keySet: Set[String]
Returns
this.prefixNamespaceMap.keySet
. -
def
minimize(declarations: Declarations): Declarations
Returns the smallest sub-declarations
decl
ofdeclarations
such thatthis.resolve(decl) == this.resolve(declarations)
-
def
minus(prefixes: Set[String]): Scope
Returns
Scope(this.prefixNamespaceMap -- prefixes)
-
final
def
ne(arg0: AnyRef): Boolean
- Definition Classes
- AnyRef
-
final
def
notify(): Unit
- Definition Classes
- AnyRef
-
final
def
notifyAll(): Unit
- Definition Classes
- AnyRef
-
def
prefixForNamespace(namespaceUri: String, getFallbackPrefix: () ⇒ String): String
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.
- val prefixNamespaceMap: Map[String, String]
-
def
prefixesForNamespace(namespaceUri: String): Set[String]
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.
-
def
relativize(scope: Scope): Declarations
Relativizes the given
Scope
against thisScope
, returning aDeclarations
object.Relativizes the given
Scope
against thisScope
, returning aDeclarations
object.Inspired by
java.net.URI
, which has a similar method for URIs. -
def
resolve(declarations: Declarations): Scope
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. -
def
resolveQNameOption(qname: QName)(implicit enameProvider: ENameProvider): Option[EName]
Tries to resolve the given
QName
against thisScope
, returningNone
for prefixed names whose prefixes are unknown to thisScope
.Tries to resolve the given
QName
against thisScope
, returningNone
for prefixed names whose prefixes are unknown to thisScope
.Note that the
subScopeOf
relation keeps theresolveQNameOption
result the same, provided there is no default namespace. That is, ifscope1.withoutDefaultNamespace.subScopeOf(scope2.withoutDefaultNamespace)
, then for each QNameqname
such thatscope1.withoutDefaultNamespace.resolveQNameOption(qname).isDefined
, we have:scope1.withoutDefaultNamespace.resolveQNameOption(qname) == scope2.withoutDefaultNamespace.resolveQNameOption(qname)
-
def
retainingDefaultNamespace: Scope
Returns an adapted copy of this Scope, but retaining only the default namespace, if any
-
def
subScopeOf(scope: Scope): Boolean
Returns true if this is a subscope of the given parameter
Scope
.Returns true if this is a subscope of the given parameter
Scope
. AScope
is considered subscope of itself. -
def
superScopeOf(scope: Scope): Boolean
Returns true if this is a superscope of the given parameter
Scope
.Returns true if this is a superscope of the given parameter
Scope
. AScope
is considered superscope of itself. -
final
def
synchronized[T0](arg0: ⇒ T0): T0
- Definition Classes
- AnyRef
-
def
toNamespaceContext: NamespaceContext
Returns the Java NamespaceContext corresponding to this Scope.
Returns the Java NamespaceContext corresponding to this Scope. Note that this method is very useful if we want to create a NamespaceContext in an easy manner. Indeed, yaidom Scopes make excellent NamespaceContext factories.
-
def
toStringInXml: String
Creates a
String
representation of thisScope
, as it is shown in XML -
final
def
wait(): Unit
- Definition Classes
- AnyRef
- Annotations
- @throws( ... )
-
final
def
wait(arg0: Long, arg1: Int): Unit
- Definition Classes
- AnyRef
- Annotations
- @throws( ... )
-
final
def
wait(arg0: Long): Unit
- Definition Classes
- AnyRef
- Annotations
- @throws( ... )
-
def
withoutDefaultNamespace: Scope
Returns an adapted copy of this Scope, but without the default namespace, if any