From 2e62a3b01ba5738c8aba979120d1a1fb2924ebf2 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 17 Oct 2009 21:31:03 +0200 Subject: [PATCH] WIP continue design how to discover session contents --- src/lib/iter-adapter.hpp | 3 +- src/proc/mobject/placement-index.hpp | 18 +++- src/proc/mobject/session/contents-query.hpp | 88 +++++++++++++++++ src/proc/mobject/session/query-resolver.hpp | 48 ++++++++++ src/proc/mobject/session/scope.hpp | 2 +- tests/43session.tests | 4 + .../proc/mobject/placement-scope-test.cpp | 37 ++++--- .../proc/mobject/scope-path-test.cpp | 1 - .../mobject/session/contents-query-test.cpp | 96 +++++++++++++++++++ wiki/renderengine.html | 54 ++++++++--- 10 files changed, 317 insertions(+), 34 deletions(-) create mode 100644 src/proc/mobject/session/contents-query.hpp create mode 100644 tests/components/proc/mobject/session/contents-query-test.cpp diff --git a/src/lib/iter-adapter.hpp b/src/lib/iter-adapter.hpp index 03d2699fe..c9d351988 100644 --- a/src/lib/iter-adapter.hpp +++ b/src/lib/iter-adapter.hpp @@ -134,7 +134,8 @@ namespace lib { * -# it should be default constructible * -# it should be copy constructible * -# when IterAdapter is supposed to be assignable, then POS should be - * -# it should provide embedded typedefs for pointer, reference and value_type + * -# it should provide embedded typedefs for pointer, reference and value_type, + * or alternatively resolve these types through a specialisation if IterTraits. * -# it should be convertible to the pointer type it declares * -# dereferencing it should yield type that is convertible to the reference type * - CON refers to the data source of this iterator (typically a data container type) diff --git a/src/proc/mobject/placement-index.hpp b/src/proc/mobject/placement-index.hpp index 362ad7c8b..98de15581 100644 --- a/src/proc/mobject/placement-index.hpp +++ b/src/proc/mobject/placement-index.hpp @@ -40,6 +40,7 @@ #include "lib/factory.hpp" #include "proc/mobject/placement.hpp" #include "proc/mobject/placement-ref.hpp" +#include "proc/mobject/session/query-resolver.hpp" #include #include @@ -47,7 +48,7 @@ #include -namespace mobject { +namespace mobject { ///////////////////////////////////////////TODO: shouldn't this go into namespace session ? using lib::factory::RefcountFac; using std::tr1::shared_ptr; @@ -60,7 +61,8 @@ namespace mobject { /** */ class PlacementIndex - : boost::noncopyable + : public session::QueryResolver ////////TODO: really inherit here? + , boost::noncopyable { class Table; @@ -88,11 +90,14 @@ namespace mobject { /** retrieve the logical root scope */ PlacementMO& getRoot() const; - /** diagnostic: number of indexed entries */ size_t size() const; bool contains (PlacementMO const&) const; bool contains (ID) const; + template + typename session::Query >::iterator + query (PlacementMO& scope) const; + /* == mutating operations == */ @@ -159,6 +164,13 @@ namespace mobject { } + template + inline typename session::Query >::iterator + PlacementIndex::query (PlacementMO& scope) const + { + UNIMPLEMENTED ("actually run the containment query"); + } + inline Placement& PlacementIndex::getScope (PlacementMO const& p) const { diff --git a/src/proc/mobject/session/contents-query.hpp b/src/proc/mobject/session/contents-query.hpp new file mode 100644 index 000000000..6e6857498 --- /dev/null +++ b/src/proc/mobject/session/contents-query.hpp @@ -0,0 +1,88 @@ +/* + CONTENTS-QUERY.hpp - query to discover the contents of a container-like part of the model + + Copyright (C) Lumiera.org + 2009, Hermann Vosseler + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +#ifndef MOBJECT_SESSION_CONTENTS_QUERY_H +#define MOBJECT_SESSION_CONTENTS_QUERY_H + +//#include "proc/mobject/mobject.hpp" +#include "proc/mobject/placement.hpp" +#include "proc/mobject/placement-index.hpp" +#include "proc/mobject/session/query-resolver.hpp" +//#include "lib/iter-adapter.hpp" + +#include +//#include +//#include + +//using std::vector; +//using std::string; + +namespace mobject { +namespace session { + + + + /** + * TODO type comment + */ + template + class ContentsQuery + : public Query > + , boost::noncopyable + { + PPIdx index_; + PlacementMO const& container_; + + iterator + runQuery() + { + UNIMPLEMENTED ("actually query the index"); + } + + + bool + isValid (Cur& pos) + { + UNIMPLEMENTED ("how to manager result set position"); + } + + + Cur const& + nextResult() + { + UNIMPLEMENTED ("how to manager result set position"); + } + + + public: + ContentsQuery (PPIdx index, PlacementMO const& scope) + : index_(index) + , container_(scope) + { } + + }; +///////////////////////////TODO currently just fleshing the API + + +}} // namespace mobject::session +#endif diff --git a/src/proc/mobject/session/query-resolver.hpp b/src/proc/mobject/session/query-resolver.hpp index da2b3a813..dc0bd6960 100644 --- a/src/proc/mobject/session/query-resolver.hpp +++ b/src/proc/mobject/session/query-resolver.hpp @@ -26,6 +26,7 @@ //#include "proc/mobject/mobject.hpp" //#include "proc/mobject/placement.hpp" +#include "lib/iter-adapter.hpp" //#include //#include @@ -36,6 +37,38 @@ namespace mobject { namespace session { + class Scope; + + /** + * TODO type comment + */ + template ///////////////////////TODO: can we get rid of that type parameter? in conjunction with the virtual functions, it causes template code bloat + class Query + { + public: + virtual ~Query() {} + + /* results retrieval */ + + typedef MO* Cur; + typedef lib::IterAdapter iterator; + + iterator operator()(){ return this->runQuery(); } + + static bool hasNext (const Query* thisQuery, Cur& pos) { return thisQuery->isValid(pos); } + static void iterNext (const Query* thisQuery, Cur& pos) { pos = thisQuery->nextResult(); } + + /* diagnostics */ + static size_t query_cnt(); + + + protected: + virtual iterator runQuery() =0; + + /* iteration control */ + virtual bool isValid (Cur& pos) const =0; + virtual Cur const& nextResult() const =0; + }; /** @@ -48,6 +81,21 @@ namespace session { virtual ~QueryResolver() {} + + /** issue a query to retrieve contents + * @param scope or container on which to discover contents + * @return an iterator to yield all elements of suitable type + * + * @todo doesn't this create a circular dependency? scope indirectly relies on QueryResolver, right?? + */ + template + typename Query::iterator + query (Scope const& scope) + { + UNIMPLEMENTED ("create a specific contents query to enumerate contents of scope"); + } + + }; ///////////////////////////TODO currently just fleshing the API diff --git a/src/proc/mobject/session/scope.hpp b/src/proc/mobject/session/scope.hpp index c4bffae06..ce3de7d86 100644 --- a/src/proc/mobject/session/scope.hpp +++ b/src/proc/mobject/session/scope.hpp @@ -60,7 +60,7 @@ namespace session { public: Scope (PlacementMO const& constitutingPlacement); - static Scope const& containing (PlacementMO const& aPlacement); + static Scope const& containing (PlacementMO const& aPlacement); //////////////TODO really returning a const& here?? static Scope const& containing (RefPlacement const& refPlacement); Scope const& getParent() const; diff --git a/tests/43session.tests b/tests/43session.tests index d5218096c..170382dee 100644 --- a/tests/43session.tests +++ b/tests/43session.tests @@ -6,6 +6,10 @@ PLANNED "AddClip_test" AddClip_test < //#include -using util::isSameObject; -//using lumiera::Time; -//using std::string; -using std::cout; -using std::endl; namespace mobject { @@ -46,9 +41,15 @@ namespace session { namespace test { // using namespace mobject::test; -// typedef TestPlacement PSub; using lumiera::error::LUMIERA_ERROR_INVALID; + using util::isSameObject; + //using lumiera::Time; + //using std::string; + using std::cout; + using std::endl; + + /*************************************************************************** * @test basic behaviour of the nested placement search scopes. @@ -77,22 +78,28 @@ namespace test { } - typedef PlacementIndex::Query::iterator _Iter; + typedef Query >::iterator _Iter; + + _Iter + getContents(PPIdx index) + { + return index->query(index->getRoot()); + } /** @test for each Placement in our test "session", * find the scope and verify it's in line with the index */ void - verifyLookup (PIdx ref_index) + verifyLookup (PPIdx ref_index) { - for (_Iter elm = ref_index.query(); elm; ++elm) + for (_Iter elm = getContents(ref_index); elm; ++elm) { ASSERT (elm->isValid()); - cout << *elm << endl; - Scope& scope1 = Scope::containing(*elm); + cout << string(*elm) << endl; + Scope const& scope1 = Scope::containing(*elm); RefPlacement ref (*elm); - Scope& scope2 = Scope::containing(ref); + Scope const& scope2 = Scope::containing(ref); // verify this with the scope registered within the index... PlacementMO& scopeTop = ref_index->getScope(*elm); @@ -107,11 +114,11 @@ namespace test { /** @test navigate to root, starting from each Placement */ void - verifyNavigation (PIdx ref_index) + verifyNavigation (PPIdx ref_index) { - for (_Iter elm = ref_index.query(); elm; ++elm) + for (_Iter elm = getContents(ref_index); elm; ++elm) { - Scope& scope = Scope::containing(*elm); + Scope const& scope = Scope::containing(*elm); ASSERT (scope == *scope.ascend()); for (Scope::iterator sco = scope.ascend(); sco; ++sco) if (sco->isRoot()) diff --git a/tests/components/proc/mobject/scope-path-test.cpp b/tests/components/proc/mobject/scope-path-test.cpp index cc05c1d3f..ddfb92da2 100644 --- a/tests/components/proc/mobject/scope-path-test.cpp +++ b/tests/components/proc/mobject/scope-path-test.cpp @@ -44,7 +44,6 @@ namespace session { namespace test { // using namespace mobject::test; -// typedef TestPlacement PSub; /*************************************************************************** diff --git a/tests/components/proc/mobject/session/contents-query-test.cpp b/tests/components/proc/mobject/session/contents-query-test.cpp new file mode 100644 index 000000000..a62cf9802 --- /dev/null +++ b/tests/components/proc/mobject/session/contents-query-test.cpp @@ -0,0 +1,96 @@ +/* + ContentsQuery(Test) - running queries to discover container contents, filtering (sub)types + + Copyright (C) Lumiera.org + 2009, Hermann Vosseler + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "lib/test/run.hpp" +//#include "lib/lumitime.hpp" +//#include "proc/mobject/placement-ref.hpp" +#include "proc/mobject/session/query-resolver.hpp" +#include "proc/mobject/session/contents-query.hpp" +#include "proc/mobject/session/test-scopes.hpp" +//#include "proc/mobject/placement-index.hpp" +//#include "lib/util.hpp" + +#include +//#include + + + +namespace mobject { +namespace session { +namespace test { + + using session::ContentsQuery; + using session::Query; + //using util::isSameObject; + //using lumiera::Time; + //using std::string; + using std::cout; + using std::endl; + + + /*************************************************************************************** + * @test how to discover the contents of a container-like part of the high-level model. + * As this container-like object is just a concept and actually implemented + * by the PlacementIndex, this includes enumerating a scope. The discovered + * contents may be additionally filtered by a runtime type check. + * + * @see mobject::PlacementIndex + * @see mobject::session::QueryResolver + * @see mobject::session::ContentsQuery + */ + class ContentsQuery_test : public Test + { + + virtual void + run (Arg) + { + + // Prepare an (test)Index backing the PlacementRefs + PPIdx index = build_testScopes(); + PlacementMO scope (index->getRoot()); + + discover (ContentsQuery (index,scope)); + discover (ContentsQuery (index,scope)); + discover (ContentsQuery (index,scope)); + discover (ContentsQuery (index,scope)); + discover (ContentsQuery(index,scope)); + } + + + template + void + discover (Query const& query) + { + Query::iterator elm = query(); + while (elm) + cout << *elm++ << endl; + } + + }; + + + /** Register this test class... */ + LAUNCHER (ContentsQuery_test, "unit session"); + + +}}} // namespace mobject::session::test diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 2c9abe508..9c5e6f979 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -1151,7 +1151,7 @@ For this to work, we need each of the //participating object types// to implemen @@clear(left):display(block):@@ -
+
Many features can be implemented by specifically configuring and wiring some unspecific components. Rather than tie the client code in need of some given feature to these configuration internals, in Lumiera the client can //query // for some kind of object providing the //needed capabilities. // Right from start (summer 2007), Ichthyo had the intention to implement such a feature using sort of a ''declarative database'', e.g. by embedding a Prolog system. By adding rules to the basic session configuration, users should be able to customize the semi-automatic part of Lumiera's behaviour to great extent.
 
 [[Configuration Queries|ConfigQuery]] are used at various places, when creating and adding new objects, as well when building or optimizing the render engine node network.
@@ -1632,7 +1632,7 @@ Besides routing to a global pipe, wiring plugs can also connect to the source po
 Finally, this example shows an ''automation'' data set controlling some parameter of an effect contained in one of the global pipes. From the effect's POV, the automation is simply a ParamProvider, i.e a function yielding a scalar value over time. The automation data set may be implemented as a bézier curve, or by a mathematical function (e.g. sine or fractal pseudo random) or by some captured and interpolated data values. Interestingly, in this example the automation data set has been placed relatively to the meta clip (albeit on another track), thus it will follow and adjust when the latter is moved.
 
-
+
This wiki page is the entry point to detail notes covering some technical decisions, details and problems encountered in the course of the implementation of the Lumiera Renderengine, the Builder and the related parts.
 
 * [[Packages, Interfaces and Namespaces|InterfaceNamespaces]]
@@ -1656,7 +1656,8 @@ Finally, this example shows an ''automation'' data set controlling some paramete
 * how to [[start the GUI|GuiStart]] and how to [[communicate|GuiCommunication]]
 * build the first LayerSeparationInterfaces
 * decide on SessionInterface and create [[Session datastructure layout|SessionDataMem]]
-* shaping the GUI/~Proc-Interface, based on MObjectRef and the [[Command frontend|CommandHandling]]
+* shaping the GUI/~Proc-Interface, based on MObjectRef and the [[Command frontend|CommandHandling]] +* defining PlacementScope in order to allow for [[discovering session contents|Query]]
!Observations, Ideas, Proposals
@@ -3051,10 +3052,10 @@ Placement references mimic the behaviour of a real placement, i.e. they proxy th
 {{red{WIP}}}
 
-
+
MObjects are attached into the [[Session]] by adding a [[Placement]]. Because this especially includes the case of //grouping or container objects,// e.g. tracks or [[meta-clips|VirtualClip]], any placement may optionally define and root a scope, and every placement is at least contained in one encompassing scope &mdash; of course with the exception of the absolute top level, which can be thought off as being contained in a scope of handling rules.
 
-Thus, while the [[sequences (former called EDL)|EDL]] act as generic container holding a pile of placments, actually there is a more fine grained structure based on the nesting of the tracks, which especially in Lumiera's HighLevelModel belong to the sequence (they aren't a property of the top level timeline as one might expect). Building upon these observations, we actually require each addition of a placement to specify a scope. Consequently, for each Placement at hand it is possible to determine an //containing scope,// which in turn is associated with some Placement of a top-level ~MObject for this scope. An example would be the {{{Placement<Track>}}} acting as scope of all the clips placed onto this track. The //implementation//&nbsp; of this tie-to-scope is provided by the same mechanism as utilised for relative placements, i.e. an directional placement relation. Actually, this relation is implemented by the PlacementIndex within the current [[Session]].
+Thus, while the [[sequences (former called EDL)|EDL]] act as generic container holding a pile of placments, actually there is a more fine grained structure based on the nesting of the tracks, which especially in Lumiera's HighLevelModel belong to the sequence (they aren't a property of the top level timeline as one might expect). Building upon these observations, we actually require each addition of a placement to specify a scope. Consequently, for each Placement at hand it is possible to determine an //containing scope,// which in turn is associated with some Placement of a top-level ~MObject for this scope. The latter is called the ''scope top''. An example would be the {{{Placement<Track>}}} acting as scope of all the clips placed onto this track. The //implementation//&nbsp; of this tie-to-scope is provided by the same mechanism as utilised for relative placements, i.e. an directional placement relation. Actually, this relation is implemented by the PlacementIndex within the current [[Session]].
 
 
 [>img[Structure of Placment Scopes|draw/ScopeStructure1.png]]
@@ -3307,7 +3308,7 @@ Besides, they provide an __inward interface__ for the [[ProcNode]]s, enabling th
 
 
-
+
The Render Engine is the part of the application doing the actual video calculations. Its operations are guided by the Objects and Parameters edited by the user in [[the EDL|EDL]] and it retrieves the raw audio and video data from the [[Data backend|backend.html]]. Because the inner workings of the Render Engine are closely related to the structures used in the EDL, this design covers [[the aspect of objects placed into the EDL|MObjects]] as well.
 <<<
 ''Status'': started out as design draft in summer '07, Ichthyo is now in the middle of a implementing the foundations and main structures in C++
@@ -3382,7 +3383,23 @@ Viewed as a micro program, the processing patterns are ''weak typed'' &mdash
 
a given Render Engine configuration is a list of Processors. Each Processor in turn contains a Graph of ProcNode.s to do the acutal data processing. In order to cary out any calculations, the Processor needs to be called with a StateProxy containing the state information for this RenderProcess
 
-
+
+
{{red{WIP as of 10/09}}}...//brainstorming about the first ideas towards a query subsystem//
+
+!use case: discovering the contents of a container in the HighLevelModel
+In the course of shaping the session API, __joel__ and __ichthyo__ realised that we're moving towards some sort of discovery or introspection. This gives rise to the quest for a //generic// pattern how to issue and run these discovery operations. The idea is to understand such a discovery as running a query &mdash; using this specific problem to shape the foundation of a query subsystem to come.
+* a ''query'' is a polymorphic, noncopyable, non-singleton type; a query instance corresponds to one distinctly issued query
+* issuing a query yields a result set, which is hidden within the concrete query implementation.
+* the transactional behaviour needs still to be defined: how to deal with concurrent modifications? COW?
+* the query instance remains property of the entity exposing the query capability.
+* client code gets a result iterator, which can be explored //only once until exhaustion.//
+* the handed out result iterator is used to manage the allocation for the query result set by sideeffect (smart handle). &rarr; Ticket #353
+
+----
+See also the notes on &rarr; QueryImplProlog
+
+
+
When querying contents of the session or sub-containers within the session, the QueryFocus follows the current point-of-query. As such queries can be issued to explore the content of container-like objects holding other MObjects, the focus is always attached to a container, which also acts as [[scope|PlacementScope]] for the contained objects. QueryFocus is an implicit state (the current point of interrest). This sate especially remembers the path down from the root of the HighLevelModel, which was used to access the current scope. Because this path constitutes a hierarchy of scopes, it can be relevant for querying and resolving placement properties. (&rarr; SessionStructureQuery)
 
 !provided operations
@@ -3393,7 +3410,7 @@ Viewed as a micro program, the processing patterns are ''weak typed'' &mdash
 * get the current ScopePath from root (session globals) down to the current scope
 [>img[Scope Locating|uml/fig136325.png]]
 !!!relation to Scope
-There is a tight integration with PlacementScope through the ScopeLocator, which establishes the //current scope.// But QueryFocus is more of a //binding// &mdash; it links or focusses the current state and into a specific scope with a ScopePath depending on the current state. Thus, while Scope is just a passive container allowing locate and navigate, QueryFocus by virtue of this binding allows to query this current location.
+There is a tight integration with PlacementScope through the ScopeLocator, which establishes the //current scope.// But QueryFocus is more of a //binding// &mdash; it links or focusses the current state and into a specific scope with a ScopePath depending on the current state. Thus, while Scope is just a passive container allowing to locate and navigate, QueryFocus by virtue of this binding allows to [[Query]] at this current location.
 
 !implementation notes
 we provide a static access API, meaning that there is a singleton behind the scenes, which manages the mentioned scope stack. Moreover, there is an link to the current session. This link works by the current session grabbing the query focus and attaching to it. This attachment is shallow, i.e. the QueryFocus doesn't have knowledge about the session, which allows the focus to be unit tested.
@@ -3404,7 +3421,7 @@ The stack of scopes must not be confused with the ScopePath. Each single frame o
 The full implementation of this scope navigation is tricky, especially when it comes to determining the relation of two positions. It should be ''postponed'' and replaced by a ''dummy'' (no-op) implementation for the first integration round.
 
-
+
//obviously, getting this one to work requires quite a lot of technical details to be planned and implemented.// This said...
 The intention is to get much more readable ("declarative") and changeable configuration as by programming the decision logic literately within the implementation of some object.
 
@@ -3773,6 +3790,17 @@ __see also__
 
 Rendering can be seen as a passive service available to the Backend, which remains in charge what to render and when. Render processes may be running in parallel without any limitations. All of the storage and data management falls into the realm of the Backend. The render nodes themselves are ''completely stateless'' &mdash; if some state is necessary for carrying out the calculations, the backend will provide a //state frame// in addition to the data frames.
+
+
A distinct property of the Lumiera application is to rely on a rules based approach rather then on hard wired logic. When it comes to deciding and branching, a [[Query]] is issued, resulting either immediately in a {{{bool}}} result, or creating a //binding// for the variables used within the query. Commonly, there is more than one solution for a given query, allowing the result set to be enumerated.
+
+
+
+
+!current state {{red{WIP as of 10/09}}}
+We are still fighting to get the outline of the application settled down.
+For now, the above remains in the status of a general concept and typical solution pattern: ''create query points instead of hard wiring things''.
+Later on we expect a distinct __query subsystem__ to emerge, presumably embedding a YAP Prolog interpreter.
+
A facility allowing the Proc-Layer to work with abstracted [[media stream types|StreamType]], linking (abstract or opaque) [[type tags|StreamTypeDescriptor]] to an [[library|MediaImplLib]], which provides functionality for acutally dealing with data of this media stream type. Thus, the stream type manager is a kind of registry of all the external libraries which can be bridged and accessed by Lumiera (for working with media data, that is). The most basic set of libraries is instelled here automatically at application start, most notably the [[GAVL]] library for working with uncompressed video and audio data. //Later on, when plugins will introduce further external libraries, these need to be registered here too.//
@@ -3792,12 +3820,12 @@ Currently as of 5/09, this is an ongoing [[implementation and planning effort|Pl {{red{TODO...}}}
-
+
"Session Interface", when used in a more general sense, denotes a compound of several interfaces and facilities, together forming the primary access point to the user visible contents and state of the editing project.
 * the API of the session class
 * the accompanying management interface (SessionManager API)
 * an LayerSeparationInterfaces allowing to access these interfaces from outside the Proc-Layer
-* the primary public ~APIs exposed on the objects to be [[queried and retrieved|SessionQueryStructure]] via the session class API
+* the primary public ~APIs exposed on the objects to be [[queried and retrieved|SessionStructureQuery]] via the session class API
 ** Timeline
 ** Sequence
 ** Placement
@@ -3854,7 +3882,7 @@ It will contain a global video and audio out pipe, just one EDL with a single tr
 &rarr; see [[Relation of Project, Timelines and Sequences|TimelineSequences]]
 
-
+
The frontside interface of the session allows to query for contained objects; it is used to discover the structure and contents of the currently opened session/project. Access point is the public API of the Session class, which, besides exposing those queries, also provides functionality for adding and removing session contents.
 
 !discovering structure
@@ -3869,7 +3897,7 @@ To give an example, let's assume a clip within a sequence, and this sequence is
 In this case, the sequence has an 1:n [[binding|BindingMO]]. A binding is (by definition) also a PlacementScope, and, incidentally, in the case of binding the sequence into a timeline, the binding also translates //logical// output designations into global pipes found within the timeline, while otherwise they get mapped onto "channels" of the virtual media used by the virtual clip. Thus, the absolute time position as well as the output connection of a given clip within this sequence //depends on how we look at this clip.// Does this clip apear as part of the global timeline, or did we discover it as contained within the meta-clip?
 
 !!solution requirements
-The baseline of any solution to this problem is clear: at the point where the query is issued, a context information is necessary; this context yields an access path from top level down to the object to be queried, and this access path constitutes the effective scope this object utilises for resolving the query.
+The baseline of any solution to this problem is clear: at the point where the query is issued, a context information is necessary; this context yields an access path from top level down to the object to be queried, and this access path constitutes the effective scope this object can utilise for resolving the query.
 
 !!introducing a QueryFocus
 A secondary goal of the design here is to ease the use of the session query interface. Thus the proposal is to treat this context and access path as part of the current state. To do so, we can introduce a QueryFocus following the queries and remembering the access path; this focus should be maintained mostly automatically. It allows for stack-like organisation, to allow sub-queries without affecting the current focus, where the handling of such a temporary sub-focus is handled automatically by a scoped local (RAII) object. The focus follows the issued queries and re-binds as necessary.