Add doclib/fix-links.py to temporarily fix/remove bad doxygen links
g0dil [Mon, 10 Sep 2007 17:53:36 +0000 (17:53 +0000)]
Add 'fixlinks' target and add 'fixlinks' to the auto-build
PPI: exchange registerEvent arguments
PPI: Add connector 'operator()' aliases
PPI: More detailed 'route' and 'registerEvent' documentation

git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@434 270642c3-0616-0410-b53a-bc976706d245

17 files changed:
Examples/RateStuffer/ratestuffer.cc
PPI/ActiveFeeder.cci
PPI/Connectors.cci
PPI/Connectors.hh
PPI/IntervalTimer.test.cc
PPI/Mainpage.dox
PPI/Module.ct
PPI/Module.hh
PPI/Module.test.cc
PPI/Route.hh
PPI/Route.test.cc
PPI/SocketReader.ct
PPI/SocketWriter.ct
SConstruct
admin/build.sh
doclib/fix-links.py [new file with mode: 0644]
senfscons/SENFSCons.py

index 9ae6bc9..8d3456e 100644 (file)
@@ -63,7 +63,7 @@ RateFilter::RateFilter(senf::ClockService::clock_type interval)
 {
     route(input,timer);
     route(timer,output);
-    registerEvent(&RateFilter::timeout, timer);
+    registerEvent(timer, &RateFilter::timeout);
 }
 
 void RateFilter::timeout()
index 2125db5..4f9b728 100644 (file)
@@ -35,7 +35,7 @@ prefix_ senf::ppi::module::ActiveFeeder::ActiveFeeder()
 {
     route(input,idle_);
     route(idle_,output);
-    registerEvent(& ActiveFeeder::request, idle_);
+    registerEvent(idle_, & ActiveFeeder::request);
 }
 
 ///////////////////////////////cci.e///////////////////////////////////////
index 5dcb08e..3c3ad79 100644 (file)
@@ -193,6 +193,11 @@ prefix_ senf::ppi::connector::ActiveConnector::ActiveConnector()
 ///////////////////////////////////////////////////////////////////////////
 // senf::ppi::connector::InputConnector
 
+prefix_ senf::Packet senf::ppi::connector::InputConnector::read()
+{
+    return operator()();
+}
+
 prefix_ senf::ppi::connector::OutputConnector & senf::ppi::connector::InputConnector::peer()
     const
 {
@@ -256,6 +261,11 @@ prefix_ void senf::ppi::connector::OutputConnector::operator()(Packet p)
     peer().enqueue(p);
 }
 
+prefix_ void senf::ppi::connector::OutputConnector::write(Packet p)
+{
+    operator()(p);
+}
+
 prefix_ senf::ppi::connector::InputConnector & senf::ppi::connector::OutputConnector::peer()
     const
 {
index f2e94aa..e42bab9 100644 (file)
@@ -255,6 +255,7 @@ namespace connector {
         typedef Queue::const_iterator queue_iterator; ///< Iterator type of the embedded queue
         typedef Queue::size_type size_type; ///< Unsigned type for counting queue elements
 
+
         Packet operator()();            ///< Get a packet
                                         /**< This member is the primary method to access received
                                              data. On passive connectors, this operator will just
@@ -265,6 +266,8 @@ namespace connector {
                                              logic error in the module implementation and an
                                              exception is raised. */
 
+        Packet read();                  ///< Alias for \ref operator()()
+
         OutputConnector & peer() const;
 
         queue_iterator begin() const;   ///< Access queue begin (head)
@@ -299,7 +302,9 @@ namespace connector {
         : public virtual Connector
     {
     public:
-        void operator()(Packet p);        ///< Send out a packet
+        void operator()(Packet p);      ///< Send out a packet
+
+        void write(Packet p);           ///< Alias for \ref operator()()
 
         InputConnector & peer() const;
 
index b3984e1..9b49d27 100644 (file)
@@ -60,7 +60,7 @@ namespace {
         :   timer ( senf::ClockService::milliseconds(d_) ), 
             n     ( n_ ) 
         {
-            registerEvent( &TimerTest::tick, timer );
+            registerEvent( timer, &TimerTest::tick );
         }
     };
 
index 2219cde..5ed72b9 100644 (file)
 
     The PPI interface is designed to be as simple as possible. It provides sane defaults for all
     configurable parameters to simplify getting started. It also automates all resource
-    management. Especially to simplify resource management, the PPI will take many configuration
-    objects by value. Even though this is not as efficient, it frees the user from most resource
-    management chores. This decision does not affect the runtime performance since it only affects
-    the configuration step.
+    management. The throttling infrastructure handles blocking conditions (like input exhaustion)
+    automatically 
 
     \section packets Packets
 
index b88a98c..e1c8fa2 100644 (file)
@@ -31,6 +31,8 @@
 ///////////////////////////////////////////////////////////////////////////
 // senf::ppi::module::Module
 
+#ifndef DOXYGEN
+
 template <class Source, class Target>
 prefix_ senf::ppi::Route<Source, Target> &
 senf::ppi::module::Module::route(Source & source, Target & target)
@@ -41,8 +43,20 @@ senf::ppi::module::Module::route(Source & source, Target & target)
                      new Route<Source,Target>(*this, source, target))));
 }
 
-template <class Target, class Descriptor>
-prefix_ void senf::ppi::module::Module::registerEvent(Target target, Descriptor & descriptor)
+#else
+prefix_ senf::ppi::Route<connector::InputConnector, connector::OutputConnector> &
+senf::ppi::module::Module::route(connector::InputConnector & input,
+                                 connector::OutputConnector & output)
+{}
+prefix_ senf::ppi::Route<connector::InputConnector, EventDescriptor> &
+senf::ppi::module::Module::route(connector::InputConnector & input, EventDescriptor & output)
+{}
+#endif
+
+#ifndef DOXYGEN
+
+template <class Descriptor, class Target>
+prefix_ void senf::ppi::module::Module::registerEvent(Descriptor & descriptor, Target target)
 {
     eventManager().registerEvent(
         *this,
@@ -51,6 +65,13 @@ prefix_ void senf::ppi::module::Module::registerEvent(Target target, Descriptor
     descriptor.enabled(true);
 }
 
+#else
+template <class Target>
+prefix_ void senf::ppi::module::Module::registerEvent(Target target,
+                                                      EventDescriptor & descriptor)
+{}
+#endif
+
 ///////////////////////////////////////////////////////////////////////////
 // senf::ppi::module::detail namespace members
 
index 31ef445..bb0de91 100644 (file)
@@ -187,37 +187,60 @@ namespace module {
                                         ///< Define flow information
                                         /**< Using the route() and noroute() members, the
                                              information flow within the module is defined. Routing
-                                             may be specified either between inputs, outputs and
-                                             events. The routing information is used to perform
-                                             automatic throttling. The throttling behavior may
-                                             however be controlled manually.
+                                             may be defined between inputs, outputs and events. The
+                                             routing information is used to perform automatic
+                                             throttling. The throttling behavior may however be
+                                             controlled manually.
 
                                              Even if no automatic throttling is desired <em>it is
-                                             vital to define the flow information for all inputs and
-                                             outputs</em>. Without flow information important
+                                             essential to define the flow information for all inputs
+                                             and outputs</em>. Without flow information important
                                              internal state of the module cannot be
                                              initialized. This includes, explicitly defining
                                              terminal inputs and outputs using noroute. Event
-                                             routing however is optional.
+                                             routing is optional however.
 
                                              The return value may be used to alter routing
                                              parameters like throttling parameters.
-                                             
+
                                              \param[in] source Data source, object which controls
                                                  incoming data (connector or event)
                                              \param[in] target Data target, object which controls
                                                  outgoing data (connector or event)
-                                             \returns Route instance describing this route */
+                                             \returns Route instance describing this route 
+                                             \note The real implementation is not provided by three
+                                                 overloads but by a single template member */
 
         Route<connector::InputConnector, EventDescriptor> &
         route(connector::InputConnector & input, EventDescriptor & output);
                                         ///< Define flow information
-                                        /**< \see \ref route() */
+                                        /**< Route from a connector to an event.  Routing from a
+                                             connector to an event defines the event as the
+                                             conceptual 'receiver' of the data. This means, the
+                                             event is controlling the processing of received data
+                                             packets (Example: Routing from an input to an IOEvent
+                                             defines, that input data will be processed whenever the
+                                             event is signaled.).
+
+                                             This event routing allows to automatically
+                                             enable/disable the event on throttling notifications. 
+
+                                             \see \ref route() */
 
         Route<EventDescriptor, connector::OutputConnector> &
         route(EventDescriptor & input, connector::OutputConnector & output);
                                         ///< Define flow information
-                                        /**< \see \ref route() */
+                                        /**< Route from an event to a connector. Routing from an
+                                             event to a connector defeines the event as the
+                                             conceptual 'source' of the data. THis means, the event
+                                             controls how packets are sent (Example: Routing from an
+                                             IOEVent to an output defines, that output data will be
+                                             generated whenever the event is signaled).
+
+                                             This event routing allows to automatically
+                                             enable/disable the event on throttling notifications. 
+
+                                             \see \ref route() */
 #endif
 
         void noroute(connector::Connector & connector); ///< Define terminal connectors
@@ -231,8 +254,12 @@ namespace module {
                                              
                                              \param[in] connector Terminal connector to declare */
 
-        template <class Target, class Descriptor>
-        void registerEvent(Target target, Descriptor & descriptor);
+#ifndef DOXYGEN
+        template <class Descriptor, class Target>
+        void registerEvent(Descriptor & descriptor, Target target);
+#else
+        template <class Target>
+        void registerEvent(Target target, EventDescriptor & descriptor);
                                         ///< Register an external event
                                         /**< The \a target argument may be either an arbitrary
                                              callable object or it may be a member function pointer
@@ -242,14 +269,14 @@ namespace module {
                                              allows to access detailed information on the event
                                              delivered.
 
-                                             The \a descriptor describes the event to signal. This
-
-                                             may be a timer event or some type of I/O event on a
-                                             file descriptor or socket.
+                                             The \a descriptor describes the event to signal like a
+                                             timer event or some type of I/O event on a file
+                                             descriptor or socket.
 
                                              \param[in] target The handler to call whenever the
                                                  event is signaled
                                              \param[in] descriptor The type of event to register */
+#endif
 
         ClockService::clock_type time() const; ///< Time-stamp of the currently processing event
                                         /**< If available, this returns the scheduled time of the
index a844abb..47795da 100644 (file)
@@ -53,7 +53,7 @@ namespace {
 
         TestModule() {
             noroute(output);
-            registerEvent(&TestModule::onEvent, event);
+            registerEvent(event, &TestModule::onEvent);
         }
 
         void onEvent() {
index 307d341..beb11e2 100644 (file)
@@ -44,6 +44,17 @@ namespace ppi {
     public:
         virtual ~RouteBase();
 
+#ifdef DOXYGEN
+        Source & source() const;        ///< Routing source
+                                        /**< \note The real implementation is in the \c
+                                             BaseRouteImplementation template in \c Route.ih. This
+                                             class is internal and not documented. */
+        Target & target() const;        ///< Routing target
+                                        /**< \note The real implementation is in the \c
+                                             BaseRouteImplementation template in \c Route.ih. This
+                                             class is internal and not documented. */
+#endif
+
     protected:
         RouteBase(module::Module & module);
 
@@ -119,9 +130,15 @@ namespace ppi {
         Route instances are created by Module::route statements. The Route class provides an
         interface to manipulate the flow processing.
 
-        The concrete interface provided depends on the type of route. If the route is a forwarding
-        route, it will be based on ForwardingRoute otherwise it will be based directly on
-        RouteBase.
+        Depending on the type of route, one of the following classes will be a baseclass:
+
+        <dl> <dt>ForwardingRoute</dt><dd>If the route is a \e forwarding route. This is a route
+        which forwards throttling notifications. This is the case, if one of the route endpoints is
+        a notify source (a connector::ActiveConnector) and the other is a
+        notify target (a connector::PassiveConnector or an EventDescriptor).
+
+        <dt>RouteBase</dt><dd>If the route is not a forwarding route, it is based directly on the
+        generic route base class</dd></dl>
      */
     template <class Source, class Target>
     class Route
index 9184960..08e5125 100644 (file)
@@ -70,7 +70,7 @@ namespace {
 
             passiveIn.onRequest(&RouteTester::inputRequest);
             passiveOut.onRequest(&RouteTester::outputRequest);
-            registerEvent(&RouteTester::onEvent, event);
+            registerEvent(event, &RouteTester::onEvent);
 
             activeIn.onThrottle(&RouteTester::throttleRequest);
             activeIn.onUnthrottle(&RouteTester::unthrottleRequest);
index 63faed0..f4a0152 100644 (file)
@@ -49,7 +49,7 @@ prefix_ senf::ppi::module::ActiveSocketReader<Reader>::
 ActiveSocketReader(Handle handle)
     : handle_(handle), event_(handle_, IOEvent::Read), reader_()
 {
-    registerEvent( &ActiveSocketReader::read, event_ );
+    registerEvent( event_, &ActiveSocketReader::read );
     route(event_, output);
 }
 
index 9c01233..29c7e30 100644 (file)
@@ -37,7 +37,7 @@ template <class Writer>
 prefix_ senf::ppi::module::ActiveSocketWriter<Writer>::ActiveSocketWriter(Handle handle)
     : handle_(handle), event_(handle_, IOEvent::Write), writer_()
 {
-    registerEvent( &ActiveSocketWriter::write, event_ );
+    registerEvent( event_, &ActiveSocketWriter::write );
     route(input, event_);
 }
 
index 449ab7c..bf5b3eb 100644 (file)
@@ -127,32 +127,38 @@ libsenf = env.Library(
 env.Default(libsenf)
 env.Alias('install_all', env.Install('$LIBINSTALLDIR', libsenf))
 
-env.AlwaysBuild(
-    env.Alias('deb', [], [ checkLocalConf,
-                           updateRevision,
-                           "$BUILDPACKAGE_COMMAND" ]))
-
-env.AlwaysBuild(
-    env.Alias('debsrc', [], [ updateRevision,
-                              "$BUILDPACKAGE_COMMAND -S" ]))
-
-env.AlwaysBuild(
-    env.Alias('debbin', [], [ checkLocalConf,
-                              updateRevision,
-                              "$BUILDPACKAGE_COMMAND -nc" ]))
-
 env.Clean('all', [ os.path.join(path,f)
                    for path, subdirs, files in os.walk('.')
                    for pattern in env['CLEAN_PATTERNS']
                    for f in fnmatch.filter(files,pattern) ])
 
-env.AlwaysBuild(env.Alias('linklint', [ 'all_docs' ], [
+env.AlwaysBuild(env.Alias('deb', [], [
+    checkLocalConf,
+    updateRevision,
+    "$BUILDPACKAGE_COMMAND" ]))
+
+env.AlwaysBuild(env.Alias('debsrc', [], [
+    updateRevision,
+    "$BUILDPACKAGE_COMMAND -S" ]))
+
+env.AlwaysBuild(env.Alias('debbin', [], [
+    checkLocalConf,
+    updateRevision,
+    "$BUILDPACKAGE_COMMAND -nc" ]))
+
+env.AlwaysBuild(env.Alias('linklint', [], [
     'rm -rf linklint',
     'linklint -doc linklint -net -limit 99999999 `find -type d -name html -printf "/%P/@ "`',
-    '[ -r linklint/errorX.html ] && python linklint_addnames.py <linklint/errorX.html >linklint/errorX.html.new',
-    '[ -r linklint/errorX.html.new ] && mv linklint/errorX.html.new linklint/errorX.html',
-    '[ -r linklint/errorAX.html ] && python linklint_addnames.py <linklint/errorAX.html >linklint/errorAX.html.new',
-    '[ -r linklint/errorAX.html.new ] && mv linklint/errorAX.html.new linklint/errorAX.html',
+    '[ ! -r linklint/errorX.html ] || python linklint_addnames.py <linklint/errorX.html >linklint/errorX.html.new',
+    '[ ! -r linklint/errorX.html.new ] || mv linklint/errorX.html.new linklint/errorX.html',
+    '[ ! -r linklint/errorAX.html ] || python linklint_addnames.py <linklint/errorAX.html >linklint/errorAX.html.new',
+    '[ ! -r linklint/errorAX.html.new ] || mv linklint/errorAX.html.new linklint/errorAX.html',
     '@echo -e "\\nLokal link check results: linklint/index.html"',
     '@echo -e "Remote link check results: linklint/urlindex.html\\n"'
 ]))
+
+env.AlwaysBuild(env.Alias('fixlinks', [ 'linklint' ], [
+    '[ ! -r linklint/errorX.txt -o ! -r linklint/errorAX.txt ] || python doclib/fix-links.py -s .svn -s linklint -s debian linklint/errorX.txt linklint/errorAX.txt',
+]))    
+
+env.Clean('all', env.Dir('linklint'))
index 98a5d4d..213e0aa 100755 (executable)
@@ -25,7 +25,7 @@ fi
 rm -f ../svn-update.log
 
 echo '$ scons -k all'
-scons -k all
+scons -k all && scons fixlinks
 echo -n '# Build completed at '; date --utc
 
 exec >../upload.log 2>&1
diff --git a/doclib/fix-links.py b/doclib/fix-links.py
new file mode 100644 (file)
index 0000000..d0df7f6
--- /dev/null
@@ -0,0 +1,231 @@
+#!/usr/bin/python
+
+import sys,os.path,fnmatch, HTMLParser, getopt
+
+class HTMLFilter(HTMLParser.HTMLParser):
+
+    def __init__(self, out=None):
+        HTMLParser.HTMLParser.__init__(self)
+        self._out = out
+        self._collect = False
+        self._data = ""
+
+    def startCollect(self):
+        self._collect = True
+        self._data = ""
+
+    def endCollect(self):
+        self._collect = False
+        return self._data
+
+    def collecting(self):
+        return self._collect
+
+    def handle_starttag(self,tag,attrs):
+        m = getattr(self,'_s_'+tag.upper(),None)
+        if m:
+            m(attrs)
+        else:
+            self.emit_starttag(tag,attrs)
+
+    def handle_endtag(self,tag):
+        m = getattr(self,'_e_'+tag.upper(),None)
+        if m:
+            m()
+        else:
+            self.emit_endtag(tag)
+
+    def handle_data(self,data):
+        if self._collect:
+            self._data += data
+        if self._out:
+            self._out.write(data)
+
+    def handle_charref(self,name):
+        self.handle_data(name)
+
+    def handle_entityref(self,name):
+        self.handle_data(name)
+
+    def emit_starttag(self,tag,attrs):
+        self.handle_data('<%s%s>' % (tag, "".join([' %s="%s"' % attr for attr in attrs])))
+
+    def emit_endtag(self,tag):
+        self.handle_data('</%s>' % tag)
+
+
+class AnchorIndex:
+
+    def __init__(self, skipdirs = ('.svn',)):
+        self._anchors = {}
+        self._skipdirs = skipdirs
+
+    def build(self):
+        sys.stderr.write("Building anchor index ")
+        nf = 0
+        for dirname, subdirs, files in os.walk('.'):
+            for d in self._skipdirs:
+                if d in subdirs:
+                    subdirs.remove(d)
+            for f in fnmatch.filter(files,'*.html'):
+                nf += 1
+                path = os.path.normpath(os.path.join(dirname, f))
+                self._addAnchor(f, path)
+                self._extractAnchors(path)
+        sys.stderr.write(" Done.\n")
+        dups = 0
+        for k in self._anchors.keys():
+            if not self._anchors[k]:
+                dups += 1
+                del self._anchors[k]
+        sys.stderr.write("%d unique anchors in %d files (%d duplicates)\n"
+                         % (len(self._anchors), nf, dups))
+
+    def _addAnchor(self, anchor, path):
+        if self._anchors.has_key(anchor):
+            self._anchors[anchor] = None
+        else:
+            self._anchors[anchor] = path
+            if len(self._anchors) % 100 == 0:
+                sys.stderr.write('.')
+
+    def __getitem__(self, key):
+        return self._anchors.get(key)
+
+    class AnchorExtractor(HTMLFilter):
+
+        def __init__(self):
+            HTMLFilter.__init__(self)
+            self._anchors = {}
+
+        def _s_A(self,attrs):
+            attrs = dict(attrs)
+            if attrs.has_key('name'):
+                self._anchors[attrs['name']] = None
+
+        def anchors(self):
+            return self._anchors.keys()
+
+    def _extractAnchors(self, f):
+        extractor = AnchorIndex.AnchorExtractor()
+        extractor.feed(file(f).read())
+        extractor.close()
+        for anchor in extractor.anchors():
+            self._addAnchor(anchor, f)
+
+
+class LinkFixer:
+
+    def __init__(self, skipdirs=('.svn',)):
+        self._index = AnchorIndex(skipdirs)
+
+    def init(self):
+        self._index.build()
+        self._files = 0
+        self._found = 0
+        self._fixed = 0
+
+    class LinkFilter(HTMLFilter):
+
+        def __init__(self, index, key, topdir, out):
+            HTMLFilter.__init__(self, out)
+            self._index = index
+            self._key = key
+            self._topdir = topdir
+            self._skip_a = False
+            self._found = 0
+            self._fixed = 0
+
+        def _s_A(self, attrs):
+            self._skip_a = False
+            if self._key in dict(attrs).get('href',''):
+                self._found += 1
+                ix = [ i for i, attr in enumerate(attrs) if attr[0] == 'href' ][0]
+                target = attrs[ix][1]
+                if '#' in target:
+                    anchor = target.split('#')[1]
+                    target = self._index[anchor]
+                    if target:
+                        target = '%s#%s' % (target, anchor)
+                else:
+                    target = self._index[os.path.split(target)[1]]
+                if target:
+                    self._fixed += 1
+                    attrs[ix] = ('href', os.path.join(self._topdir,target))
+                else:
+                    self._skip_a = True
+                    return
+            self.emit_starttag('a',attrs)
+
+        def _e_A(self):
+            if self._skip_a:
+                self._skip_a = False
+            else:
+                self.emit_endtag('a')
+
+        def stats(self):
+            return (self._found, self._fixed)
+
+    def fix(self, path, target):
+        self._files += 1
+        data = file(path).read()
+        filt = LinkFixer.LinkFilter(self._index,
+                                    target,
+                                    "../" * (len(os.path.split(path)[0].split("/"))),
+                                    file(path,"w"))
+        filt.feed(data)
+        filt.close()
+        self._found += filt.stats()[0]
+        self._fixed += filt.stats()[1]
+
+    def stats(self):
+        return (self._files, self._found, self._fixed)
+    
+
+(opts, args) = getopt.getopt(sys.argv[1:], "s:")
+if len(args) != 2:
+    sys.stderr.write("""Usage:
+       fix-links.py [-s skip-dir]... <errrorX.txt> <errorAX.txt>
+
+Process the 'errorX.txt' and 'errorAX.txt' files as generated by
+'linklint': Check all invalid links and try to find the correct
+target. If a target is found, the link is changed accordingly,
+otherwise the link is removed.
+
+To find anchors, fix-links.py generates a complete index of all
+anchors defined in any HTML file in the current directory or some
+subdirectory. The directories named 'skiped-dir' (at any level) will
+not be scanned for '*.html' files.
+""")
+    sys.exit(1)
+
+skipdirs = [ val for opt, val in opts if opt == '-s' ]
+
+fixer = LinkFixer(skipdirs)
+fixer.init()
+
+target = None
+for l in file(args[0]):
+    l = l.rstrip()
+    if l.startswith('/'):
+        target = '#' + os.path.split(l)[1]
+    elif l.startswith('    /') and not l.endswith('/'):
+        sys.stderr.write("%s\n" % l)
+        fixer.fix(l[5:], target)
+
+for l in file(args[1]):
+    l = l.rstrip()
+    if l.startswith('/'):
+        target = l.split('#')[1]
+    elif l.startswith('    /') and not l.endswith('/'):
+        sys.stderr.write("%s\n" % l)
+        fixer.fix(l[5:], target)
+
+files, found, fixed = fixer.stats()
+
+sys.stderr.write("""
+Files processed : %5d
+Links processed : %5d
+Links fixed     : %5d
+Links removed   : %5d
+""" % (files, found, fixed, found-fixed))
index 4245fa9..74e035d 100644 (file)
@@ -118,7 +118,7 @@ def Finalizer(f):
 # libraries are provided by the distribution, you probably don't need
 # to specify any parameters. If your configuration is more complex,
 # refer to the <a
-# href="http://www.boost.org/tools/build/v1/build_system.htm">Boost.Build</a>
+# href="http://www.boost.org/tools/build/v2/index.html">Boost.Build</a>
 # documentation for a definition of the terms used above (toolset,
 # variant, runtime ...).
 #