#####################################################################
#####################################################################
## Copyright 2005 Steven R. Newcomb                                ##
##                                                                 ##
## This software is licensed under the Apache License, Version 2.0 ##
## (the "License"); you may not use this file except in compliance ##
## with the License.  Copies of the License are available at       ##
##        http://www.apache.org/licenses/LICENSE-2.0               ##
##                                                                 ##
## Unless required by applicable law or agreed to in writing,      ##
## software distributed under the License is distributed on an "AS ##
## IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either ##
## express or implied.  See the License for the specific language  ##
## governing permissions and limitations under the License.        ##
##                                                                 ##
## Copies of the source code and the license are available at      ##
##        http://www.versavant.org                                 ##
#####################################################################
#####################################################################

####################################
cvsRevision = "$Revision: 1.6 $" ##
####################################

import versavant, sys
    ## Tell Python we're going to use the "versavant" and "sys"
    ## modules.  The Versavant file (either versavant.py or
    ## versavant.pyc) needs to be in a directory that appears in your
    ## PYTHONPATH environment variable.

#######################################################
def main():
    ## This function is where this Python script actually begins
    ## (to see that that is so, check the bottom of this file).

        ## Get a Versavant object, so we can use the Versavant API.
    vsv = versavant.Versavant()

        ## Tell Versavant to create a topic map whose name is 'Topic Map 1'
    vsv.newTopicMap( 'Topic Map 1', "first demo topic map")

        ## Tell Versavant to load the implementation of the Topic Map
        ## Application (TMA) that is specified by the configuration
        ## file whose path is 'DemoTMA1/tmaconf.xml'. Also, in the
        ## context of topic map "Topic Map 1", tell Versavant to
        ## consider that the unique name of this TMA will be 'TMA1'.
        ## If you want to understand what Versavant is, and how it
        ## allows independent ontologies ("TMAs") to govern a single
        ## topic map, you will want to look at the files in the
        ## directory DemoTMA1/tmaconf.xml/.
    vsv.loadTMA( 'Topic Map 1', 'TMA1', 'DemoTMA1/tmaconf.xml')

        ## Register an original population task -- a function that,
        ## when called by Versavant, will insert subject proxies into
        ## the topic map.
    origPopTaskObject = vsv.registerOriginalPopulationTask(

            'Topic Map 1',        ## i.e., 'Topic Map 1'

            'demo.py',            ## This file (this file!) is where the
                                  ## original population task (OPT)
                                  ## function can be found.

            'origPopTask1',       ## The name of the OPT function

            'ProxifyHuckBook'     ## The name of the OPT task.  This name
                                  ## will appear in the auditing records.

            )


        ## Tell Versavant that now is the time to perform the original
        ## population task you just registered.  Versavant will now
        ## call the origPopTask1() function.  You might want to
        ## go take a look at that function now.
    vsv.populate( origPopTaskObject)

        ## The following code will print (to stdout) a display of the
        ## proxies whose subject is the book 'The Adventures of Huckleberry Finn by Mark
        ## Twain', as that subject is identified under TMA1.  Two
        ## things are worth noting, here:
        ## 
        ## * The TMA, not Versavant, determines how subjects are
        ##   identified, and how to determine whether two proxies have
        ##   the same subject (i.e., whether their SIP values match).
        ##   For its Book properties, TMA1 simply does a string
        ##   match. (See function
        ##   ProxiesOfSubjectIdentifiedBy_Book_Value() in
        ##   DemoTMA1/PropertyClasses.py.)
        ## 
        ## * At this point in this demo, no merging has occurred, and
        ##   since the original population task created two proxies
        ##   for the book (just to make things interesting), those two
        ##   proxies are both returned.
    proxyList = vsv.livingProxiesWithMatchingPropertyValue(
        'Topic Map 1', ## "Topic Map 1"  -- the topic map we're working on
        'TMA1',        ## The name of the TMA we loaded into Topic Map 1
        'Book',        ## The TMA1-defined property class whose instances
                       ## appear as subject identity properties (SIPs) in
                       ## proxies whose subjects are books.
        'The Adventures of Huckleberry Finn by Mark Twain'
                       ## TMA "TMA1" regards this string as a
                       ## sufficient expression of the identity of the
                       ## book.  In a way, this example is bogus, but
                       ## in another way, it's realistic.  It's
                       ## realistic in the sense that it's sometimes
                       ## desirable to reflect, in a topic map,
                       ## exactly what came from the source from which
                       ## it was built.  The sources from which topic
                       ## maps need to be built may identify their
                       ## subjects in many ways, and not all of those
                       ## ways will be sophisticated; they could be as
                       ## naive as "The Adventures of Huckleberry Finn by Mark Twain".
                       ## They may require special handling, just as
                       ## "The Adventures of Huckleberry Finn by Mark Twain" requires
                       ## special handling by this TMA's conferral
                       ## rule (see below).
        )
    print 'proxies for "The Adventures of Huckleberry Finn by Mark Twain" before merging:'
    for proxy in proxyList:
        print vsv.showProxy( proxy)


        ## Below, we tell Versavant that now is the time to perform
        ## all merging and conferral operations specified by all the
        ## TMAs that have been loaded.  This is where Topic Maps in
        ## general -- and the Versavant Disclosure Discipline in
        ## particular -- earn their keep.  Each time we call the
        ## merge() function, we know that afterwards, within the topic
        ## map, all TMAs are fully satisfied.  This means that:
        ## 
        ## * There is only one subject proxy for each subject (all
        ##   required merges have been completed), and
        ##   
        ## * All subject proxies and properties that any TMA regarded
        ##   as logically implicit have been made explicit (all
        ##   conferral rules are satisfied).
    vsv.merge( 'Topic Map 1')


        ## Now that we've done a merge operation, we can show that
        ## there is only one living proxy for the book "Huckleberry
        ## Finn".  Note in the output that this proxy is neither of
        ## the original proxies.  They are both "dead" now, but they
        ## still exist, serving as auditable records of how the
        ## currently "living" proxy came into existence.  (You'll be
        ## able to see those auditing records when run 
        ## inspect(), later.)
    proxyList = vsv.livingProxiesWithMatchingPropertyValue(
        'Topic Map 1',  ## "Topic Map 1"  -- the topic map we're working on
        'TMA1',         ## The name of the TMA we loaded into Topic Map 1
        'Book',        ## The TMA1-defined property class whose instances
                       ## appear as subject identity properties (SIPs) in
                       ## proxies whose subjects are books.
        'The Adventures of Huckleberry Finn by Mark Twain'
                       ## TMA "TMA1" regards this string as a
                       ## sufficient expression of the identity of the
                       ## book.
        )
    print 'proxies for "The Adventures of Huckleberry Finn by Mark Twain" after merging:'
    for proxy in proxyList:      ## show all the proxies on the list
        print vsv.showProxy( proxy)

        ## Now let's go exploring in the topic map we've made,
        ## beginning with a look at the list of proxies that we have
        ## created.  Aim your browser at the http address that is
        ## output after "Now serving at..."  When done exploring the
        ## topic map, click on "Stop Server", and this demo program
        ## will resume.
##     vsv.inspect( proxy = proxyList[ 0],
##                  showSamples = True,
##                  showThumbnails = True)

    ## Note: What are "system proxies"?
    ## 
    ##       Every Versavant topic map has a subject proxy for each of
    ##       the subjects that Versavant itself has to keep track of:
    ##       
    ##       * a proxy for the topic map itself,
    ##       
    ##       * a proxy for each of the TMAs that govern that topic map,
    ##         and a proxy for each of the following things defined by
    ##         each TMA:
    ##       
    ##         + a proxy for each value type
    ##       
    ##         + a proxy for each property class, and
    ##       
    ##         + a proxy each conferral rule.
    ##       
    ##       * a proxy for each original population task, including the
    ##         one that created these basic proxies when you called
    ##         newTopicMap().
    ##
    ##       The properties of all of these "system proxies" are
    ##       governed by the Versavant System TMA, called "Vsys".
    ##       Vsys is a TMA like any other, except that its
    ##       implementation is a significant fraction of the
    ##       implementation of Versavant itself.  The implementation
    ##       of Vsys includes much of the software that bootstraps
    ##       topic maps into existence, and that handles the
    ##       registration and management of TMAs and original
    ##       population tasks.  (The Vsys implementation can be found
    ##       in the VsysTMA directory.)
    ##
    ##       So, every Versavant topic map is born with about 100
    ##       proxies.


    ## Note: Where did the proxy for Harper & Brothers come from?
    ##       It was "conferred into existence" by the conferral rule
    ##       "inferFromBookProperties", defined by the DemoTMA1 TMA.
    ##
    ##       (The implementation of this conferral rule can be found
    ##       in DemoTMA1/inferFromBookProperties.py; the declaration
    ##       of the rule is found in DemoTMA1/tmaconf.xml.)



        ## Tell Versavant to export the topic map as an XML document
        ## called 'Topic Map 1.xml'.  (You'll find it there.)  The
        ## exported XML topic map not only represents the (living)
        ## subject proxies; it also incorporates and encapsulates the
        ## TMAs that govern it.  It cannot be misinterpreted on
        ## arrival; it says how it manages the addressing, merging,
        ## conferral of its subjects.  Even more importantly, it is
        ## ready to execute that same management in the context of
        ## whatever larger topic map anyone wants to merge it with.
    vsv.exportXMLTopicMap( 'Topic Map 1', 'Topic Map 1.xml')

        ## Tell Versavant to import the same topic map.  This is, of
        ## course, a rather silly thing to do, except perhaps for
        ## demonstration purposes.  Since we're importing the same
        ## topic map that was just exported, many subject proxies will
        ## now be duplicated.
    vsv.importXMLTopicMap( 'Topic Map 1', 'Topic Map 1.xml', 'import Topic Map 1')

    ## Note: The process of importing a Versavant XML topic map has two
    ##       phases.  First, the system proxies are imported, but only
    ##       their Vsys properties are imported.  This first phase has the
    ##       effect of registering the TMAs of the topic map being
    ##       imported.  With the TMAs loaded and ready to function, then
    ##       all the proxies are loaded, including a second copy of each
    ##       system proxy.  This time, though, their non-Vsys properties,
    ##       if any, are also imported.  The extra proxies will be killed
    ##       and replaced the next time merge() is called.

        ## Tell Versavant that, once again, now is the time to perform
        ## all merging and conferral operations specified by all the
        ## TMAs that have been loaded.
    vsv.merge( 'Topic Map 1')

######################################################
## You might want to call vsv.inspect() again here so
## you can look at Topic Map 1.
######################################################

        ## Now lets create another topic map, "Topic Map 2".
    vsv.newTopicMap( 'Topic Map 2', "second demo topic map")

        ## Load a different TMA, calling it 'TMA2'.  The TMA that is
        ## disclosed in the directory 'DemoTMA2/tmaconf.xml' defines
        ## properties for subjects who are human beings, and it allows
        ## them to have various kinds of names.  TMA1 and TMA2 are
        ## completely independent of each other, and they don't
        ## know anything about each other.
    vsv.loadTMA( 'Topic Map 2', 'TMA2', 'DemoTMA2/tmaconf.xml')

        ## Register an original population task.
    origPopTaskObject = vsv.registerOriginalPopulationTask(

            'Topic Map 2',          ## i.e., 'Topic Map 1'

            'demo.py',            ## This file (this file!) is where the
                                  ## original population task (OPT)
                                  ## function can be found.

            'origPopTask2',       ## The name of the original population
                                  ## task function

            'ProxifySamClemens'   ## The name of the original population 
                                  ## task.  This name will appear in the
                                  ## auditing records.

            )

        ## Execute the original population task.
    vsv.populate( origPopTaskObject)

        ## It doesn't hurt to merge it, but nothing is going to happen
        ## when we do, because no two proxies in Topic Map 2 have the
        ## same subject, and TMA2 doesn't have any conferral rules.
    vsv.merge( 'Topic Map 2')

        ## We now export Topic Map 2 as an XML document.
    vsv.exportXMLTopicMap( 'Topic Map 2', 'Topic Map 2.xml')


######################################################
## You might want to call vsv.inspect() again here so
## you can look at Topic Map 2.
######################################################


    ## Now comes the most interesting part.  We now have two
    ## completely independent sources of information that have been
    ## represented as topic maps: Topic Map 1.xml and Topic Map 2.xml.
    ## Ontologically speaking, they have nothing in common: no logic
    ## in common, and no terminology in common.  The only thing they
    ## have in common is that both are topic maps, and both of their
    ## TMAs are disclosed by them under the Versavant Disclosure
    ## Discipline.  The Versavant Disclosure Discipline is a way of
    ## meeting the disclosure requirements for subject addressability
    ## that are set forth in the draft ISO/IEC 13250 Topic Maps
    ## Reference Model.

    ## Let's now pretend that we have somehow obtained these two topic
    ## maps, and we want to combine ("merge") them into a single topic
    ## map, in which there is only one subject proxy per subject.

        ## First, we need to create a topic map in Versavant in which
        ## to do this merging:
    vsv.newTopicMap( 'Topic Map 3', "third demo topic map")

        ## Now let's import both topic maps:
    vsv.importXMLTopicMap( 'Topic Map 3', 'Topic Map 1.xml', 'import Topic Map 1')
    vsv.importXMLTopicMap( 'Topic Map 3', 'Topic Map 2.xml', 'import Topic Map 2')

    ## Note that we have now loaded both TMA1 and TMA2, because
    ## TMA1 was imported with Topic Map 1, and TMA2 was imported
    ## with Topic Map 2.  The TMAs include their implementations.

    ## We call merge() now, but the only proxies that will be merged
    ## are some system proxies that are always duplicated when TMAs
    ## are imported from XML topic maps.  Versavant won't merge TMA1's
    ## "Mark Twain" proxy with TMA2's "Samuel Langhorne Clemens"
    ## proxy, because neither TMA1 nor TMA2 knows anything about the
    ## other's property classes.
    vsv.merge( 'Topic Map 3')

######################################################
## You might want to call vsv.inspect() again here so
## you can look at Topic Map 3 before we do anything else.
#######################################################

    ## This is where the Versavant Disclosure Discipline shows its
    ## power.  We are now in a position to take advantage of the work
    ## that was done to give addresses to subjects in Topic Map 1 in
    ## terms of TMA1, and in Topic Map 2 in terms of TMA2,
    ## simultaneously.  It is possible to create another TMA, which
    ## we'll call 'TMA3', that, in light of the full disclosure about
    ## the way that TMA1 and TMA2 identify subjects, regards some of
    ## the proxies that they separately govern as having the same
    ## subject, and therefore should be merged.

    ## TMA3 is a so-called "bridging TMA".  Bridging TMAs define one
    ## or more conferral rules that confer instances of the property
    ## classes that *they* define, on the basis of instances of
    ## property classes that they do *not* define -- that are defined
    ## by other TMAs.  Our TMA3 defines two conferral rules, 'person
    ## name aggregation 1' and 'person name aggregation 2'.  Both
    ## rules are implemented in DemoTMA3/personNameAggregation.py, as
    ## specified in DemoTMA3/tmaconf.xml, by functions
    ## personNameAggregation1() and personNameAggregation2().

    ## One of the rules confers TMA3-defined 'Person Name List'
    ## properties on the basis of TMA1-defined 'PersonOrOrganization'
    ## property instances.  The other rule confers TMA3-defined
    ## 'Person Name List' properties on the basis of TMA2-defined
    ## 'Person', 'Birth Full Name', and 'Aliases' properties.  TMA3's
    ## 'Person Name List' properties are SIPs that are regarded as
    ## indicating the same subject if they have any identifier in
    ## common.

    ## (Yes, this is simplistic.  This is a demo.  Neither the
    ## Versavant Disclosure Discipline nor this Versavant.py
    ## implementation of it constrains the bases, methods, or
    ## algorithms that a TMA can use to recognize subject sameness.
    ## You can define your TMAs any way you like.  You can exploit the
    ## subject identifications that anyone has made, in any Versavant
    ## topic map, in any way you like.)

        ## All we have to do now is load TMA3 and call merge(), and
        ## there will be only one proxy for both Mark Twain and Samuel
        ## Clemens, because TMA3 has, in effect, regarded both TMA1's
        ## proxy for Mark Twain and TMA2's proxy for Samuel Clemens as
        ## representing one and the same subject.
    vsv.loadTMA( 'Topic Map 3', 'TMA3', 'DemoTMA3/tmaconf.xml')
    vsv.merge( 'Topic Map 3')

        ## Just for fun, let's export Topic Map 3, too:
    vsv.exportXMLTopicMap( 'Topic Map 3', 'Topic Map 3.xml')


    ## find out which proxy has Mark Twain/Sam Clemens as its subject:
    proxyList = vsv.livingProxiesWithMatchingPropertyValue(
        'Topic Map 3',  ## "Topic Map 1"  -- the topic map we're working on
        'TMA3',       ## The name of the TMA we loaded into Topic Map 1
        'Person Name List',      ## The TMA1-defined property class whose instances
                       ## appear as subject identity properties (SIPs) in
                       ## proxies whose subjects are books.
        [ 'Mark Twain'],
                       ## TMA "TMA3" regards this string as a
                       ## sufficient expression of the identity of the
                       ## subject.
        )
    print 'proxies for "The Adventures of Huckleberry Finn by Mark Twain" at the very end:'
    for proxy in proxyList:
        print vsv.showProxy( proxy)


        ## Let's inspect Topic Map 3, starting at
        ## the proxy for Mark Twain/Samuel Clemens.
    vsv.inspect(
       proxy = vsv.livingProxiesWithMatchingPropertyValue(

            'Topic Map 3',  ## The name of the topic map we're working on

            'TMA3',       ## The name of the TMA that defines the
                          ## 'Book' property class

            'Person Name List',     ## property class

            [ 'Mark Twain'],  ## property value
        )[ 0],            ## [ 0] means "only the first item on on the list"
                          ## of proxies returned by
                          ## livingProxiesWithMatchingPropertyValue()"
       showSamples = True, showThumbnails = True
    )

    ## Demo ends here.


#######################################################
def origPopTask1( vsv, origPopTaskObject):
    ## This function implements the original population task we
    ## registered in main(), calling it 'ProxifyHuckBook'.

    ## IMPORTANT: Please understand that this is a simple
    ## demonstration for tutorial purposes.  Original population tasks
    ## usually generate subject proxies and their properties
    ## programmatically, by reading some incoming data stream, by
    ## querying databases, etc.  Such programs use the same API
    ## demonstrated here.  (They may use more of the features of the
    ## Versavant API than are demonstrated here.)

    ## We're only creating a single subject proxy here, in order
    ## to keep the demo simple.

        ## Create a subject proxy in the topic map whose name is
        ## 'Topic Map 1'.  (Versavant knows which topic map because that
        ## information was registered in the
        ## origPopTaskObject.)  The subject of this proxy is the
        ## going to be the book "The Adventures of Huckleberry Finn" by Mark Twain.
    proxyForHuckBook = vsv.newProxyObject()

        ## Create an instance of the property class 'Book' giving it
        ## the value 'The Adventures of Huckleberry Finn by Mark Twain', and add this
        ## new property instance to the proxy that was just created
        ## (here locally called 'proxyForHuckBook').
    vsv.newPropertyInstance(
        proxyForHuckBook,  ## (We just created this proxy object.)

        'TMA1',             ## The name of the TMA that defines
                            ## the property class of which this
                            ## property is an instance.

        'Book',           ## The name of the property class that
                            ## we're instantiating
        
        'The Adventures of Huckleberry Finn by Mark Twain'
                            ## The value we're giving to this
                            ## property instance.
        )

        ## Create a proxy for the book "The Adventures of Tom Sawyer" by Mark Twain.
    proxyForTomSawyerBook = vsv.newProxyObject()
    vsv.newPropertyInstance(
        proxyForTomSawyerBook,
        'TMA1',
        'Book',        
        'The Adventures of Tom Sawyer by Mark Twain')

        ## Just for demo purposes, redundantly create another proxy
        ## with the same subject.
    proxy2ForTomSawyerBook = vsv.newProxyObject()
    vsv.newPropertyInstance(
        proxy2ForTomSawyerBook,
        'TMA1',
        'Book',        
        vsv.versaCopy( 'The Adventures of Tom Sawyer by Mark Twain'))

    vsv.newPropertyInstance(
        proxy2ForTomSawyerBook,
        'TMA1',
        'BookPublisherName',        
        'Harper & Brothers')

    ## Note: The property class 'Book', like all property
    ##       classes in this particular original population task, is
    ##       defined/implemented by the TMA that was loaded as 'TMA1',
    ##       and that is defined by the TMA configuration file
    ##       'DemoTMA1/tmaconf.xml'.  This property class is an "SIP"
    ##       class -- a "Subject Identity Property" class.  That means
    ##       that it specifies the subject of the proxy, and it will
    ##       be the basis on which decisions will be made as to
    ##       whether this proxy will be merged with other proxies --
    ##       whether other proxies have the same subject as this one.
    ##       The way in which the decision is made is determined by
    ##       the implementation of the property class, which was
    ##       loaded along with the rest of 'TMA1'.  The function that
    ##       makes that determination is
    ##       MergeProxiesBecause_Person_Name_SIP_PropertyInstancesSpecifySameSubject(),
    ##       found in DemoTMA1/PropertyClasses.py.


#######################################################
def origPopTask2( vsv, origPopTaskObject):
    ## This function implements the original population task we
    ## registered in main(), calling it 'ProxifySamClemens'.

    proxyForSamClemens = vsv.newProxyObject()

        ## Create an instance of the SIP class 'Person'
        ## giving it the value '022-1321-A29-ZR 22X', and add
        ## this new property instance to the proxy that was just
        ## created (here locally called 'proxyForSamClemens').
    vsv.newPropertyInstance(
        proxyForSamClemens,  ## (We just created this proxy object.)

        'TMA2',             ## The name of the TMA that defines
                            ## the property class of which this
                            ## property is an instance.

        'Person',   ## The name of the property class that
                               ## we're instantiating
        
        '022-1321-A29-ZR 22X'
                            ## The value we're giving to this
                            ## property instance.
        )


        ## Create an instance of the property class 'Birth Full Name'
        ## giving it the value 'Samuel Langhorne Clemens', and add
        ## this new property instance to the proxy that was just
        ## created (here locally called 'proxyForSamClemens').
    vsv.newPropertyInstance(
        proxyForSamClemens,  ## (We just created this proxy object.)

        'TMA2',             ## The name of the TMA that defines
                            ## the property class of which this
                            ## property is an instance.

        'Birth Full Name',  ## The name of the property class that
                            ## we're instantiating
        
        'Samuel Langhorne Clemens'
                            ## The value we're giving to this
                            ## property instance.
        )


        ## Create an instance of the property class 'Aliases'
        ## giving it the value "[ 'Mark Twain']", and add
        ## this new property instance to the proxy that was just
        ## created (here locally called 'proxyForSamClemens').
    vsv.newPropertyInstance(
        proxyForSamClemens,  ## (We just created this proxy object.)

        'TMA2',             ## The name of the TMA that defines
                            ## the property class of which this
                            ## property is an instance.

        'Aliases',          ## The name of the property class that
                            ## we're instantiating
        
        [ 'Mark Twain'],
                            ## The value we're giving to this
                            ## property instance.  This is a Python
                            ## list object that contains one item,
                            ## the string 'Mark Twain'.
        )



#######################################################
if __name__ == "__main__":
    ## If this script has been invoked directly, as opposed to being
    ## imported by another script...

    main()  ## ...execute function main()
    ## (This is a Python convention.)

