couchdb-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From j..@apache.org
Subject [3/52] [partial] Add contributed documentation from janl's couchdb-docs repository.
Date Mon, 30 Apr 2012 17:33:21 GMT
http://git-wip-us.apache.org/repos/asf/couchdb/blob/1a82fd7c/share/docs/DocKit/Makefile.core
----------------------------------------------------------------------
diff --git a/share/docs/DocKit/Makefile.core b/share/docs/DocKit/Makefile.core
new file mode 100644
index 0000000..0b7dc7c
--- /dev/null
+++ b/share/docs/DocKit/Makefile.core
@@ -0,0 +1,3 @@
+# Set the relative directory for tools repo
+
+COUCHDB_TOOLS ?= $(COUCHDB_ROOT)/../couchdocs-tools

http://git-wip-us.apache.org/repos/asf/couchdb/blob/1a82fd7c/share/docs/DocKit/bin/CouchDocs.pm
----------------------------------------------------------------------
diff --git a/share/docs/DocKit/bin/CouchDocs.pm b/share/docs/DocKit/bin/CouchDocs.pm
new file mode 100644
index 0000000..f4824d9
--- /dev/null
+++ b/share/docs/DocKit/bin/CouchDocs.pm
@@ -0,0 +1,1277 @@
+package CouchDocs;
+
+use Carp qw(croak cluck confess);
+use File::Basename;
+use IO::File;
+use Data::Dumper;
+use strict;
+use warnings;
+use Module::Load;
+use Module::Util qw/:all/;
+use File::Spec::Functions;
+use DateTime;
+use Cwd;
+
+sub new
+{
+    my ($self,$opts) = @_;
+
+    my $class = ref($self) || $self;
+
+    return bless {
+        _modulename => 'CouchDocs',
+        global_opts => $opts,
+        dependencies => {},
+        module_map => {
+            'urlapi' => 'CouchDocs::URLAPI',
+            'json'   => 'CouchDocs::JSON',
+            'config' => 'CouchDocs::Config',
+        },
+        module_cache => {
+        },
+        metatext => [],
+    };
+}
+
+# Take a string, look for metadocs insertion statements, load the corresponding module
+# and then parse and insert the content
+
+sub metaparse
+{
+    my ($self,$srcfile,$destfile) = @_;
+
+    my $srctext = $self->slurp_file($srcfile);
+
+    my $blddestfile = $destfile;
+    $blddestfile =~ s/\.tmp$//;
+
+    $self->{dependencies}->{$blddestfile}->{$srcfile} = 1;
+    $self->{tool_dependencies}->{$blddestfile}->{$self->resolve_module_path('CouchDocs')} = 1;
+
+    my @metaelements;
+    my ($protection_counter,$protection_max) = (0,10);
+
+    @metaelements = get_meta_statements($srctext);
+
+    while(scalar @metaelements > 0)
+    {
+        my $metaelement = pop @metaelements;
+
+# Metasettings contain the specific info;
+
+        my $metasettings = {};
+
+        extract_meta_settings($metaelement,\$metasettings);
+
+# If there is a version specification in the metasettings
+# then check if the value is 'inherit', and use the line option
+
+        if (exists($metasettings->{version}) &&
+            $metasettings->{version} eq 'inherit')
+        {
+            $metasettings->{version} = $self->{global_opts}->{version};
+        }
+
+# There must be a meta type in the specification
+        if (!exists($metasettings->{type}) &&
+            !defined($metasettings->{type}))
+        {
+            croak("You must specify a meta-type");
+        }
+
+        my $type = $metasettings->{type};
+        my $module = $self->{module_map}->{$type};
+
+        if (!exists($self->{module_map}->{$type}) ||
+            !defined($module))
+        {
+            $self->meta_error("Cannot find module for metadoc type $type\n");
+        }
+
+        if (!exists($self->{module_cache}->{$type}) &&
+            !defined($self->{module_cache}->{$type}))
+        {
+            load $module;
+
+            $self->{module_cache}->{$type} = eval "$module->new();";
+
+            $self->{tool_dependencies}->{$blddestfile}->{$self->resolve_module_path($module)} = 1;
+
+            croak "Couldn't initiate $self->{module_map}->{$type}"
+                unless (defined($self->{module_cache}->{$type}));
+
+            $self->{module_cache}->{$type}->set_global_opt($self->{global_opts});
+        }
+
+        my $response = $self->{module_cache}->{$type}->parse($metasettings);
+
+        push(@metaelements,get_meta_statements($response->{text}));
+        my $quotedelement = quotemeta($metaelement);
+
+        my $replacetext = '';
+        if (exists($response->{embedded_dependencies}) &&
+            $response->{embedded_dependencies} == 1)
+        {
+            $replacetext = $response->{text};
+            my $depend = join("\n",
+                              $self->generate_dependencies('meta',$response->{dependencies}),
+                              $self->generate_dependencies('tool',$self->{tool_dependencies}->{$blddestfile}));
+            $replacetext =~ s/<!--DEPENDBLOCK-->/$depend/;
+        }
+        else
+        {
+            $replacetext = join("\n",
+                                $self->generate_dependencies('meta',$response->{dependencies}),
+                                $self->generate_dependencies('tool',$self->{tool_dependencies}->{$blddestfile}),
+                                $response->{text});
+        }
+
+        $srctext =~ s/$quotedelement/$replacetext/msg;
+    }
+
+    return if (exists($self->{global_opts}->{depend}) &&
+               ($self->{global_opts}->{depend} = 1));
+
+    $srctext = $self->remap_dynxml_source($srctext);
+
+    $self->write_file($destfile,$srctext);
+}
+
+# Validate a given XML source
+
+sub metavalidate
+{
+    my ($self,$xmlfile) = @_;
+
+    my $module =  $self->{module_map}->{$self->{global_opts}->{metatype}};
+
+    load $module;
+
+    my $validate  = eval "$module->new();";
+
+    $validate->set_global_opt($self->{global_opts});
+
+    $validate->validate($xmlfile);
+}
+
+sub remap_dynxml_source
+{
+    my ($self,$string) = @_;
+
+    $string =~ s{(!#!amp!#!(.*?)!#!;!#!)}{&$2;}msg;
+
+    return($string);
+}
+
+sub generate_dependencies
+{
+    my ($self,$type,$depends) = @_;
+
+    my @dependlist;
+
+    foreach my $file (keys %{$depends})
+    {
+        push(@dependlist,sprintf('<remark role="dependency-%s" condition="%s"/>',
+                                 $type,
+                                 $file));
+    }
+    return(@dependlist);
+}
+
+sub resolve_module_path
+{
+    my ($self,$module) = @_;
+
+    my $dirname = dirname($0);
+    $module =~ s{::}{/}g;
+    $module .= '.pm';
+    return "$dirname/$module";
+}
+
+# Perform any final clean up and formatting on a sourcefile
+
+sub finalparse
+{
+    my ($self,$srcfile,$dstfile) = @_;
+
+    my $filetext = $self->slurp_file($srcfile);
+
+# Replace the metaversion tag with the version from the makefile
+
+    $filetext =~ s/__meta_version__/$self->{global_opts}->{version}/msg;
+    my $verid = $self->format_version_as_docbook($self->{global_opts}->{version});
+    $filetext =~ s/__meta_version_id__/$verid/msg;
+
+# Build and changes dates
+    my $builddt = DateTime->from_epoch(epoch => $^T);
+    my $builddate = sprintf('%d %s %d %d:%d',
+                            $builddt->day,
+                            $builddt->month_abbr,
+                            $builddt->year,
+                            $builddt->hour,
+                            $builddt->minute);
+
+    my $maxdate = 0;
+
+    foreach my $file (split(/ /,$self->{global_opts}->{filelist}))
+    {
+        next if ($file =~ m{/common/});
+        my $filedate = (stat $file)[9];
+        
+        $maxdate = $filedate
+            if ($filedate > $maxdate);
+    }
+
+    $builddt = DateTime->from_epoch(epoch => $maxdate);
+    my $changedate = sprintf('%02d %s %04d %02d:%02d',
+                             $builddt->day,
+                             $builddt->month_abbr,
+                             $builddt->year,
+                             $builddt->hour,
+                             $builddt->minute);
+
+    $filetext =~ s/__meta_builddate__/$builddate/msg;
+    $filetext =~ s/__meta_changedate__/$changedate/msg;
+
+# Remap any DynXML elements 
+
+    $filetext = $self->remap_dynxml_source($filetext);
+
+    $self->write_file($dstfile,$filetext);
+}
+
+# Dependency parser
+# This takes the information populated during parsing metadocs and
+# individual modules (which is itself tracked by adding the file
+# names/paths into a hash structure
+# The code also has to follow any imports or requirements loaded from
+# other files
+# We also have to handle the fact that certain files are essentially
+# global dependencies for the root target (for example, image files)
+# This is because we must be able to determine a list of image files
+# to be copied into target directories
+
+sub dependparse
+{
+    my ($self,$filename,$parentfile) = @_;
+    $parentfile = $filename if (!defined($parentfile));
+
+    my @dlist; 
+
+    push(@dlist,
+         {
+             realfile => $filename,
+             srcfile => $parentfile
+         });
+
+    while (my $dlistentry = pop @dlist)
+    {
+        my ($realfile,$srcfile) = ($dlistentry->{realfile},
+                                   $dlistentry->{srcfile});
+
+        next if (exists($self->{dependencyparsed}->{$realfile}));
+
+        if (!-f $realfile)
+        {
+            my $build_command = sprintf('%s --directory %s %s',
+                                        $ENV{MAKE} || 'make',
+                                        dirname($realfile),
+                                        basename($realfile)
+                );
+            system($build_command);
+            croak "Couldn't find/build $srcfile: $?"
+                if (!-f $srcfile);
+        }
+
+        $self->{dependencies}->{$srcfile} = {} unless (exists($self->{dependencies}->{$srcfile}));
+        $self->{dependencies}->{$srcfile}->{$realfile} = 1;
+        
+        $self->{dependencies}->{_xml}->{catfile($self->{global_opts}->{buildsrc},$realfile)} = 1;
+        
+        if ($realfile !~ m/metadoc-(.*?).xml/)
+        {
+            $self->{dependencies}->{_xmlbase}->{catfile($self->{global_opts}->{buildsrc},$realfile)} = 1;
+        }
+        
+        my $filetext = $self->slurp_file($realfile);
+        
+# Pick out dependency remarks
+        
+        my (@remarks) = ($filetext =~ m/<remark role="dependency-tool" condition="([^"]+)"\/>/msg);
+        
+        foreach my $remark (@remarks)
+        {
+            $self->{dependencies}->{catfile($self->{global_opts}->{buildsrc},dirname($srcfile),$realfile)}->{$remark} = $remark;
+        }
+        (@remarks) = ($filetext =~ m/<remark role="dependency-source" condition="([^"]+)"\/>/msg);
+        
+        foreach my $remark (@remarks)
+        {
+            $self->{dependencies}->{catfile($self->{global_opts}->{buildsrc},dirname($srcfile),$realfile)}->{$remark} = $remark;
+            
+            push(@dlist,
+                 { 
+                     realfile => catfile($self->{global_opts}->{buildsrc},dirname($realfile),$remark),
+                     srcfile => $remark
+                 }
+                );
+        }
+        (@remarks) = ($filetext =~ m/<remark role="dependency-meta" condition="([^"]+)"\/>/msg);
+        
+        foreach my $remark (@remarks)
+        {
+            $self->{dependencies}->{catfile($self->{global_opts}->{buildsrc},dirname($srcfile),$realfile)}->{$remark} = $remark;
+        }
+        
+# Pick out xi:include
+# An xinclude of a metadoc requires us to parse the -metasrc file, not the metadoc- file
+
+        my (@includes) = ($filetext =~ m/xi:include.*?href="([^"]+)"/msg);
+
+        foreach my $includesrc (@includes)
+        {
+            if ($includesrc =~ m/metadoc-(.*?).xml/)
+            {
+                my $sourcefile = catfile($self->{global_opts}->{buildsrc},
+                                         dirname($realfile),
+                                         dirname($includesrc),
+                                         sprintf('%s-metasrc.xml',$1));
+                $self->{dependencies}->{_xml}->{$sourcefile} = 1;
+                $self->{dependencies}->{_xmlbase}->{$sourcefile} = 1;
+            }
+            # Parse the extracted include file ($include), using the directory of the file to parse ($realfile) and the
+            # current build directory, with $realfile as the parent
+            
+            my $includefile = catfile($self->{global_opts}->{buildsrc},dirname($realfile),$includesrc);
+            
+            push(@dlist,
+                 {
+                     realfile => $includefile,
+                     srcfile => $realfile
+                 }
+                );
+        }
+        
+# Pick out images
+        
+        my (@images) = $filetext =~ m/<imagedata.*?fileref="([^"]+)"/msg;
+        
+        foreach my $image (@images)
+        {
+            my $imageloc = catfile($self->{global_opts}->{buildsrc},dirname($realfile),$image);
+            $self->{dependencies}->{$srcfile}->{$imageloc} = 1;
+            $self->{dependencies}->{_globalimages}->{$imageloc} = 1;
+        }
+        
+        $self->{dependencyparsed}->{$realfile} = 1;
+    }
+}
+
+# Copies the global options through into the object
+
+sub set_global_opt
+{
+    my ($self,$options) = @_;
+
+    $self->{global_opts} = $options;
+}
+
+# meta_object_filter takes an object and settings specification
+# and returns true or false based on whether all the filter specifications
+# and object entries match
+#
+# We also check the settings match the version
+
+sub meta_object_filter
+{
+    my ($self,$settings,$object) = @_;
+
+# Check the versions first
+
+    if (exists($settings->{version}))
+    {
+        if ($self->meta_verify_version($settings->{version},
+                                       $object) == 0)
+        {
+            return(0);
+        }
+    }
+
+# Filters are specified in the metasettings structure (as read
+# from the remarks) using the filter_ prefix
+# the suffix in each case should be a hash entry in the supplied
+# object against which we can match
+
+    my $filtercount = 0;
+    my $matchcount = 0;
+
+    foreach my $id (keys %{$settings})
+    {
+        next unless ($id =~ m/^filter_(.*)/);
+        my $filterfield = $1;
+        $filtercount++;
+
+# If the filter field doesn't exist, then we can't filter it (so it's valid)
+
+        if (exists($object->{$filterfield}))
+        {
+            if (ref($object->{$filterfield}) eq 'HASH')
+            {
+                $matchcount++
+                    if (exists($object->{$filterfield}->{$settings->{$id}}));
+            }
+            else
+            {
+                $matchcount++
+                    if ($object->{$filterfield} eq $settings->{$id});
+            }
+        }
+        else
+        {
+            $matchcount++;
+        }
+    }
+    if ($filtercount == $matchcount)
+    {
+        return(1);
+    }
+    return(0);
+}
+
+# meta_verify_version checks the following tags in a structure
+# and returns true or false if the version spec matches the
+# supplied version. Tags are:
+#
+# iv (in version) - the version where the item was first available
+# ov (out version) - the version where the item became unavilable
+# rv (deleted version) - the version when it was removed (implies ov)
+# dv (deprecated version) - still available but not recommended
+
+sub meta_verify_version
+{
+    my ($self,$checkversion,$checkobject) = @_;
+
+# Default is for the item not to match
+
+    my $available = 0;
+
+# Versions are inclusive, and can include either a range
+# or an explicit list of versions
+
+    my ($lowerversion,$upperversion,@versionlist);
+
+    if ($checkversion =~ m/,/)
+    {
+        @versionlist = split(/,/,$checkversion);
+    }
+    else
+    {
+        push @versionlist,$checkversion;
+    }
+
+    foreach my $version (@versionlist)
+    {
+        if ($version =~ m/(.*)-(.*)/)
+        {
+            ($lowerversion,$upperversion) = ($1,$2);
+        }
+        else
+        {
+            ($lowerversion,$upperversion) = ($version,$version);
+        }
+        if ($self->meta_verify_version_explicit($lowerversion,$upperversion,$checkobject) == 1)
+        {
+            $available = 1;
+        }
+    }
+    return($available);
+}
+
+# Verify an explicit version
+
+sub meta_verify_version_explicit
+{
+    my ($self,$lowerversion,$upperversion,$checkobject) = @_;
+
+     my $available = 1;
+
+# If we dont have an in or out version, short circuit a valid response
+
+    if (!exists($checkobject->{iv}) &&
+        !exists($checkobject->{ov}) &&
+        !defined($checkobject->{iv}) &&
+        !defined($checkobject->{ov})
+        )
+    {
+        return $available;
+    }
+
+# If the upper version is less than the inversion, 
+# then we haven't made it yet, ergo no match
+
+    if ((exists($checkobject->{iv})) &&
+        ($self->vertodec($upperversion) <
+         $self->vertodec($checkobject->{iv})))
+    {
+        $available = 0;
+    }
+
+# If the lowerversion is greater than the outversion,
+# then we are beyond the version, ergo no match
+
+    if ((exists($checkobject->{ov})) &&
+        ($self->vertodec($lowerversion) >=
+         $self->vertodec($checkobject->{ov})))
+    {
+        $available = 0;
+    }
+
+    return ($available);
+}
+
+# The metatext system holds strings temporarily while building
+# metadocs content
+
+# Empties the metatext array
+
+sub metatext_empty
+{
+    my ($self) = @_;
+
+    $self->{metatext} = [];
+}
+
+# Returns the metatext buffer as a string
+
+sub metatext_asstring
+{
+    my ($self) = @_;
+
+    my @final;
+
+    foreach my $ent (@{$self->{metatext}})
+    {
+        next if (!defined($ent));
+        push @final,$ent;
+    }
+    return(join('',@final));
+}
+
+# print wrapper for metatext
+
+sub metatext_print
+{
+    my ($self) = shift;
+
+    push(@{$self->{metatext}},@_);
+}
+
+# printf wrapper for metatext
+
+sub metatext_printf
+{
+    my ($self,$format,@args) = @_;
+
+    confess("No format specified") if (!defined($format));
+    push(@{$self->{metatext}},sprintf($format,@args));
+}
+
+# Get the individual elements of a meta statement
+# Extract an ID if it exists
+# Extract <remark>, info is either
+# <remark role="FIELDNAME" condition="VALUE">
+# For settings
+# or
+# <remark role="FIELDNAME">VALUE</remark>
+# For DocBook values or titles
+
+sub extract_meta_settings
+{
+    my ($metaelement,$metasettings) = @_;
+
+    my ($targetid) = ($metaelement =~ m/id="([^\"]*?)"/);
+    $$metasettings->{id} = $1if (defined($targetid));
+
+    foreach my $remark ($metaelement =~ m{<remark\s+?role=".*?"\s+?condition=".*?"/>}gms)
+    {
+        $remark =~ m{<remark\s+?role="([^"]*)"\s+?condition="([^"]*)"/>}gms;
+        $$metasettings->{$1} = $2;
+    }
+    foreach my $remark ($metaelement =~ m{<remark\s+?role="[^"]*">.*?</remark>}gms)
+    {
+        $remark =~ m{<remark\s+?role="([^"]*)">(.*?)</remark>}gms;
+        $$metasettings->{$1} = $2;
+    }
+}
+
+# Parse some text and pull out the meta elements that need parsing
+#
+# Items are marked with role="meta"
+
+sub get_meta_statements
+{
+    my ($text) = @_;
+    my @elements;
+
+    push(@elements,
+         ($text =~ m{(<(?:para|title|phrase)(?:\s+(?:id="|role="meta)[^"]*")+(?:/>|>.*?</(?:para|title|phrase)>))}msg));
+
+    my @retelems;
+
+    foreach my $elem (@elements)
+    {
+        if ($elem =~ m/role="meta"/)
+        {
+            push @retelems,$elem;
+        }
+    }
+
+    return(@retelems);
+}
+
+sub slurp_file
+{
+    my ($self,$filename,$clean) = @_;
+
+    $clean = 1 unless(defined($clean));
+
+    my $fh = new IO::File($filename) or croak("Can't open source file ($filename): $!\n");
+    binmode($fh,':utf8');
+    my $inplacefile = join('',<$fh>);
+    $fh->close();
+
+# Remove commented XML
+    $inplacefile =~ s{<!--.*?-->}{}msg;
+
+# Escape entities
+# These are escaped during parsing to ensure they aren't lost
+    $inplacefile =~ s{(&([a-zA-Z0-9_-]*?);)}{!#!amp!#!$2!#!;!#!}msg;
+                                                 return($inplacefile);
+}
+
+sub write_file
+{
+    my ($self,$filename,$string) = @_;
+
+    my $fh = new IO::File($filename,'w')
+        or die "Error: Can't open destination file ($filename: $!\n";
+    binmode($fh,':utf8');
+    syswrite($fh,$string);
+    $fh->close();
+}
+
+# Take a multipart version number and then convert it into
+# a numerical value so that it can be compared and sorted
+
+sub vertodec
+{
+    my ($self,$version) = @_;
+
+    confess($version) unless(defined($version));
+
+    my ($a,$b,$c,$d) = (0,0,0,'');
+
+    my $multiplier = 1000;
+
+    ($a,$b,$c) = split(m/\./,$version,3);
+
+    if (!defined($b))
+    {
+        $b = 0;
+    }
+
+    if (!defined($c))
+    {
+        $c = 0;
+    }
+
+    if ($c =~ m/(\d+)(.*)/)
+    {
+        $c = $1;
+        $d = $2;
+    }
+
+    croak "$version not numeric" unless($a == int($a) &&
+                                        $b == int($b) &&
+                                        $c == int($c));
+
+    my $decimalversion = $multiplier**4 +
+        (($a * $multiplier**3) +
+         ($b * $multiplier**2) +
+         ($c * $multiplier) +
+         ord($d));
+
+    return ($decimalversion);
+}
+
+# Convert a decimalized version number back into a multipart
+# version number
+
+sub dectover
+{
+    my ($self,$version) = @_;
+
+    my ($a,$b,$c,$d) = ($version =~ m/1(\d{3})(\d{3})(\d{3})(\d{3})/);
+
+    return sprintf('%0d.%0d.%0d%s',$a,$b,$c,($d == 0 ? '' : chr($d)));
+}
+
+# Generate an XML tag with or without attributes
+
+sub attrib_builder
+{
+    my ($attrib) = @_;
+
+    return(join(' ',map { "$_=\"$attrib->{$_}\"" } keys %{$attrib}));
+}
+
+sub xml_tag
+{
+    my $self = shift;
+    my $tagname = shift;
+
+    if ((scalar @_ == 0) ||
+        (!defined($_[0])))
+    {
+        return "<$tagname/>";
+    }
+
+    if (ref($_[0]) eq 'HASH')
+    {
+        return sprintf('<%s %s>%s</%s>',
+                       $tagname,
+                       attrib_builder($_[0]),
+                       join('',@_[1..$#_]),
+                       $tagname);
+    }
+    else
+    {
+        return sprintf('<%s>%s</%s>',
+                       $tagname,
+                       join('',@_),
+                       $tagname);
+    }
+}
+
+sub xml_entry
+{
+    my $self = shift;
+    return $self->xml_tag('entry',@_);
+}
+
+sub xml_row
+{
+    my $self = shift;
+    return $self->xml_tag('row',@_);
+}
+
+sub xml_literal
+{
+    my $self = shift;
+    return $self->xml_tag('literal',@_);
+}
+
+sub format_version_as_docbook
+{
+    my ($self,$string) = @_;
+
+    $string =~ s/\./\-/g;
+
+    return($string);
+}
+
+# Merge docbook link
+# 
+# Take a list of ID/location entities and merge them using _
+
+sub merge_docbook_link
+{
+    my ($self,@elements) = @_;
+
+    my @final;
+    foreach my $el (@elements)
+    {
+        push @final,$el if (defined($el));
+    }
+
+    return(lc(join('_',@final)));
+}
+
+# Resolve DocBook links to the right place
+
+sub resolve_docbook_link
+{
+    my ($self,$id) = @_;
+
+    return($id);
+}
+
+# Global function for generating a metadoc
+# table
+#
+# Tables consist of the table definition, and the rows as a array;
+# each row should be a row for the cells in order
+
+sub generate_metadoc_table
+{
+    my ($self,$tabledef,$rows,$options) = @_;
+
+    $self->generate_metadoc_table_header($tabledef);
+
+    if (defined($options) && exists($options->{postsort}))
+    {
+        foreach my $row (sort { $a->[$options->{postsort}] cmp 
+                                    $b->[$options->{postsort}] } @{$rows})
+        {
+            $self->generate_metadoc_tablerow($row);
+        }
+    }
+    else
+    {
+        foreach my $row (@{$rows})
+        {
+            $self->generate_metadoc_tablerow($row);
+        }
+    }
+    $self->metatext_printf('</tbody></tgroup></%s>',$tabledef->{type});
+}
+
+# Take a list of values as a row
+# The row should be an array of cells
+
+sub generate_metadoc_tablerow
+{
+    my ($self,$row) = @_;
+
+    $self->metatext_printf('<row>');
+    foreach my $cell (@{$row})
+    {
+        if (ref($cell) eq 'HASH')
+        {
+            $self->metatext_print($self->xml_entry($cell->{attr},$cell->{content}));
+        }
+        else
+        {
+            $self->metatext_print($self->xml_entry($cell));
+        }
+    }
+    $self->metatext_printf('</row>');
+}
+
+# Create the table header
+# We take the definition, adding the ID
+# then output the thead group with the titles
+# and then setup the content for the actual rows
+
+sub generate_metadoc_table_header
+{
+    my ($self,$definition) = @_;
+
+    $self->metatext_printf('<%s%s%s>',
+                          $definition->{type},
+                          (exists($definition->{id}) &&
+                           defined($definition->{id}) ?
+                           sprintf(' id="%s"',$definition->{id}) : ''),
+                          (exists($definition->{class}) &&
+                           defined($definition->{class}) ?
+                           sprintf(' class="%s"',$definition->{class}) : ''),
+        );
+
+    if ($definition->{type} eq 'table' &&
+        exists($definition->{title}) &&
+        defined($definition->{title}) && 
+        ($definition->{title} =~ m/[a-zA-Z0-9]/))
+    {
+        $self->metatext_printf('<title>%s</title>',$definition->{title});
+    }
+    elsif ($definition->{type} eq 'informaltable' &&
+           exists($definition->{title}) &&
+           defined($definition->{title}) && 
+           ($definition->{title} =~ m/[a-zA-Z0-9]/))
+    {
+        $self->metatext_printf('<textobject><phrase>%s</phrase></textobject>',$definition->{title});
+    }
+    if ($definition->{type} eq 'table' &&
+        exists($definition->{title}) &&
+        defined($definition->{title}) && 
+        ($definition->{title} !~ m/[a-zA-Z0-9]/))
+    {
+        croak(sprintf('Title not specified for a table that requires it (%s)',$definition->{id} || ''));
+    }
+
+    my @columnseq = ();
+
+    foreach my $colid (sort { ($definition->{collist}->{$a}->{_so} || 0) <=>
+                                  ($definition->{collist}->{$b}->{_so} || 0) } keys %{$definition->{collist}})
+    {
+        push @columnseq,$colid;
+    }
+
+    my $columncount = 0;
+
+    foreach my $colid (@columnseq)
+    {
+        next if (exists($definition->{columns}->{$colid}->{namest}));
+        $columncount++;
+    }
+
+    $self->metatext_printf('<tgroup cols="%d">',$columncount);
+
+    my @titles = ();
+
+    foreach my $colid (@columnseq)
+    {
+        next if (exists($definition->{collist}->{$colid}->{namest}));
+        my $alignment = $definition->{collist}->{$colid}->{align} or undef;
+        my $width = $definition->{collist}->{$colid}->{width} or undef;
+        my $widthsuffix = '';
+
+        $widthsuffix = '*' if (defined($width) &&
+                               ($width =~ m/^[0-9]+$/));
+
+        $self->metatext_printf('<colspec colname="%s"%s%s/>',
+                          $colid,
+                          (defined($alignment) ? sprintf(' align="%s"',$alignment) : ''),
+                          (defined($width) ? sprintf(' colwidth="%s%s"',$width,$widthsuffix) : ''),
+            );
+        push(@titles,$self->xml_entry($definition->{collist}->{$colid}->{title}));
+    }
+
+    unless (exists($definition->{nocolhead}) &&
+            $definition->{nocolhead} == 1)
+    {
+        if (exists($definition->{multiheader}))
+        {
+            my @rows = ();
+            foreach my $rowheadspec (sort { $definition->{multiheader}->{$a}->{so} <=>
+                                                $definition->{multiheader}->{$b}->{so} }
+                                     keys %{$definition->{multiheader}})
+            {
+                my @row = ();
+
+                foreach my $col (sort { $definition->{collist}->{$a}->{so} <=>
+                                            $definition->{collist}->{$b}->{so} }
+                                 @{$definition->{multiheader}->{$rowheadspec}->{fields}})
+                {
+                    my $colopts = {};
+                    foreach my $opt (qw/morerows namest nameend/)
+                    {
+                        $colopts->{$opt} = $definition->{collist}->{$col}->{$opt}
+                        if (exists($definition->{collist}->{$col}->{$opt}));
+                    }
+                    push @row,$self->xml_entry($colopts,$definition->{collist}->{$col}->{title});
+                }
+                push @rows,$self->xml_row(@row);
+            }
+            $self->metatext_print('<thead>',
+                                  @rows,
+                                  '</thead>');
+        }
+        else
+        {
+            $self->metatext_print('<thead>',
+                                  $self->xml_row(@titles),
+                                  '</thead>');
+        }
+    }
+
+    $self->metatext_printf('<tbody>');
+}
+
+sub validate_object_version
+{
+    my ($self,$identity,$object) = @_;
+
+    if (exists($object->{iv}) &&
+        exists($object->{ov}) &&
+        ($self->vertodec($object->{ov}) <
+         $self->vertodec($object->{iv}))
+        )
+    {
+        $self->meta_warning(sprintf('Object %s: Out version is less than In version',$identity));
+    }
+    if (exists($object->{dv}) &&
+        exists($object->{ov}) &&
+        ($self->vertodec($object->{dv}) >
+         $self->vertodec($object->{ov}))
+        )
+    {
+        $self->meta_warning(sprintf('Object %s: Deprecated version is greater than Out version',$identity));
+    }
+    if (exists($object->{rv}) &&
+        exists($object->{ov}) &&
+        ($self->vertodec($object->{rv}) >
+        $self->vertodec($object->{ov}))
+        )
+    {
+        $self->meta_warning(sprintf('Object %s: Deprecated version is greater than Removed version',$identity));
+    }
+}
+
+sub describe_object_version
+{
+    my ($self,$object) = @_;
+
+    my $struct = {};
+
+    if (exists($object->{iv}))
+    {
+        $struct->{introduced} = sprintf('Introduced v%s',$object->{iv});
+        $struct->{introduced_short} = sprintf('Int. v%s',$object->{iv});
+        $struct->{introduced_vonly} = $object->{iv};
+    }
+    if (exists($object->{ov}))
+    {
+        $struct->{removed} = sprintf('Removed v%s',$object->{ov});
+        $struct->{removed_short} = sprintf('Removed v%s',$object->{ov});
+        $struct->{removed_vonly} = $object->{ov};
+    }
+    if (exists($object->{dv}))
+    {
+        $struct->{deprecated} = sprintf('Deprecated v%s',$object->{dv});
+        $struct->{deprecated_short} = sprintf('Dep. v%s',$object->{dv});
+        $struct->{deprecated_vonly} = $object->{dv};
+    }
+
+    return($struct);
+}
+
+sub describe_object_version_astext
+{
+    my ($self,$object,$type) = @_;
+
+    $type = '' unless(defined($type));
+    $type = ''if (($type ne 'short') ||
+                  ($type ne 'vonly'));
+    
+    my $vs = $self->describe_object_version($object);
+
+    my @s;
+    foreach my $vtype (qw/introduced deprecated removed/)
+    {
+        push(@s,$vs->{sprintf('%s%s',$vtype,$type)})
+            if (exists($vs->{$vtype}));
+    }
+
+    if (scalar(@s) == 0)
+    {
+        return undef;
+    }
+    else
+    {
+        return(join(', ',@s));
+    }
+}
+
+sub meta_error
+{
+    my ($self,$text) = @_;
+
+    printf STDERR ("Error: %s: %s\n",
+                  $self->{_modulename} || 'CouchDocs',
+                  $text
+          );
+    exit(1);
+}
+
+sub meta_warning
+{
+    my ($self,$text) = @_;
+
+    printf STDERR ("Warning: %s: %s\n",
+                   $self->{_modulename} || 'CouchDocs',
+                   $text
+        );
+}
+
+sub deep_copy
+{
+    my $self = shift;
+    my $this = shift;
+    if (not ref $this) {
+        $this;
+    } elsif (ref $this eq 'ARRAY') {
+        [map $self->deep_copy($_), @$this];
+    } elsif (ref $this eq 'HASH') {
+        +{map { $_ => $self->deep_copy($this->{$_}) } keys %$this};
+    } else { die 'what type is $_?' }
+}
+
+sub setbyattrib
+{
+    my ($self,$item,$element,$list) = @_;
+
+    foreach my $list_item (@{$list})
+    {
+        if (exists($element->{Attributes}->{$list_item}))
+        {
+            $$item->{$list_item} = $element->{Attributes}->{$list_item};
+        }
+    }
+}
+
+sub escape_string
+{
+    my ($rawtext) = @_;
+
+    $rawtext =~ s/&/&amp;/g;
+    $rawtext =~ s/&amp;([a-z_]);/&$1;/g;
+    $rawtext =~ s/</&lt;/g;
+    $rawtext =~ s/>/&gt;/g;
+
+    return($rawtext);
+}
+
+sub characters
+{
+    my ($self, $element) = @_;
+#    $element->{Data} =~ s/[\r\n]+/ /g;
+    while ($element->{Data} =~ s/[ ][ ]+/ /g) {}
+
+    if ($self->{captext})
+    {
+        $self->{currenttext} .= escape_string($element->{Data});
+    }
+}
+
+sub entity_reference
+{
+    my ($self,$element) = @_;
+
+    $self->{currenttext} . sprintf('&%s;',$element->{Name}) if ($self->{savecdata});
+}
+
+sub remap_xml_enable
+{
+    my ($self,$element) = @_;
+
+    $self->{remapxmldata} = 1;
+    $self->{captext} = 1;
+}
+
+sub remap_xml_disable
+{
+    my ($self,$element) = @_;
+
+    $self->{remapxmldata} = 0;
+    $self->{captext} = 0;
+}
+
+sub remap_xml_start
+{
+    my ($self,$element) = @_;
+
+    if ($self->{remapxmldata})
+    {
+        $self->{currenttext} .= sprintf('<%s',$element->{Name});
+
+        foreach my $attrib (keys %{$element->{Attributes}})
+        {
+            $self->{currenttext} .= sprintf(' %s="%s"',$attrib,$element->{Attributes}->{$attrib});
+        }
+        $self->{currenttext} .= '>';
+
+        return 1;
+    }
+    return 0;
+}
+
+sub remap_xml_end
+{
+    my ($self,$element) = @_;
+
+    if ($self->{remapxmldata})
+    {
+        $self->{currenttext} .= sprintf('</%s>',$element->{Name});
+        return 1;
+    }
+    return 0;
+}
+
+sub reset_parser_charbuffer
+{
+    my ($self) = @_;
+
+    my $text = $self->{currenttext};
+    $self->{currenttext} = '';
+    $self->{captext} = 0;
+    return($text);
+}
+
+sub get_xml_region
+{
+    my ($self) = @_;
+    return join(' -> ',grep(/_xmlregion_/,keys %{$self}));
+}
+
+sub check_xml_region
+{
+    my ($self,$element) = @_;
+    return($self->{xml_region_format($element)});
+}
+
+sub check_xml_mregion
+{
+    my ($self,@elems) = @_;
+
+    my $count = 0;
+    my $noncount = 0;
+
+    my $checklist = {};
+
+    foreach my $element (@elems)
+    {
+        my $felement = xml_region_format($element);
+        $checklist->{$felement} = 1;
+    }
+
+    foreach my $element (@{$self->{xmlregions}})
+    {
+        my $felement = xml_region_format($element);
+
+        if (exists($checklist->{$felement}) &&
+            $self->{$felement} == 1)
+        {
+            $count++;
+        }
+        else
+        {
+            return(0);
+        }
+    }
+    return(1);
+}
+
+sub set_xml_region
+{
+    my ($self,$element) = @_;
+
+    foreach my $check (@{$self->{xmlregions}})
+    {
+        if ($check eq $element)
+        {
+            my $felement = xml_region_format($element);
+            $self->{$felement} = 1;
+        }
+    }
+}
+
+sub xml_region_format
+{
+    my ($region) = @_;
+
+    return(sprintf('_xmlregion_%s',$region));
+}
+
+sub unset_xml_region
+{
+    my ($self,$element) = @_;
+
+    foreach my $check (@{$self->{xmlregions}})
+    {
+        $self->{xml_region_format($element)} = 0
+            if ($check eq $element);
+    }
+}
+
+1;

http://git-wip-us.apache.org/repos/asf/couchdb/blob/1a82fd7c/share/docs/DocKit/bin/CouchDocs/Config.pm
----------------------------------------------------------------------
diff --git a/share/docs/DocKit/bin/CouchDocs/Config.pm b/share/docs/DocKit/bin/CouchDocs/Config.pm
new file mode 100644
index 0000000..f8e3311
--- /dev/null
+++ b/share/docs/DocKit/bin/CouchDocs/Config.pm
@@ -0,0 +1,227 @@
+package CouchDocs::Config;
+
+use strict;
+use warnings;
+use Carp;
+use XML::Parser::PerlSAX;
+
+use Data::Dumper;
+
+use vars qw/@ISA/;
+@ISA = ('CouchDocs');
+
+use CouchDocs::Config::Parser;
+
+sub new
+{
+    my ($self) = @_;
+
+    my $class = ref($self) || $self;
+
+    return bless {
+        _modulename => 'CouchDocs::Config',
+        meta_opts => {
+            basedir => 'config',
+        },
+        metacache => {},
+        filecache => {},
+    };
+}
+
+sub parse
+{
+    my ($self,$metasettings) = @_;
+
+    my $retval = {text => '',
+                  _files => ''};
+
+    $self->load_metasrc($metasettings);
+
+    $self->metatext_empty();
+
+    if ($metasettings->{output} eq 'classsummarytable')
+    {
+        $self->classsummarytable($metasettings);
+    }
+    if ($metasettings->{output} eq 'optionsummarytable')
+    {
+        $self->optionsummarytable($metasettings);
+    }
+    if ($metasettings->{output} eq 'itemtable')
+    {
+        $self->itemtable($metasettings);
+    }
+
+    $retval->{dependencies} = $self->{dependencies};
+    $retval->{text} = $self->metatext_asstring();
+
+    return $retval;
+}
+
+sub validate
+{
+    my ($self,$metafile) = @_;
+
+    $self->load_metasrc({src => $metafile});
+}
+
+sub classsummarytable
+{
+    my ($self,$metasettings) = @_;
+
+    my $tabledef = {
+        title => $metasettings->{title},
+        id => $metasettings->{id} || undef,
+        type => 'table',
+        collist => {
+            section => {
+                _so => 1,
+                title => 'Section',
+            },
+            desc => {
+                _so => 3,
+                title => 'Description',
+            },
+        },
+    };
+
+    my @rows;
+
+    my $td = $self->{metacache}->{$metasettings->{src}}->{classes};
+
+    foreach my $structureid (sort { ($td->{$a}->{description} or $a) cmp
+                                        ($td->{$b}->{description} or $b) } keys %{$td})
+    {
+        next unless ($self->meta_object_filter($metasettings,$td->{$structureid}));
+        my @row;
+
+        push(@row,
+             (exists($metasettings->{idprefix}) && defined($metasettings->{idprefix}) ? 
+              $self->xml_tag('link',
+                             {
+                                 linkend => 
+                                     $self->merge_docbook_link($metasettings->{idprefix},
+                                                               $structureid),
+                             },
+                             $self->xml_literal($structureid)) : $self->xml_literal($structureid)),
+             $td->{$structureid}->{description});
+        
+        push @rows,\@row;
+    }
+    $self->generate_metadoc_table($tabledef,\@rows); 
+}
+
+sub optionsummarytable
+{
+    my ($self,$metasettings) = @_;
+
+    my $tabledef = {
+        title => $metasettings->{title},
+        id => $metasettings->{id} || undef,
+        type => 'table',
+        collist => {
+            option => {
+                _so => 1,
+                title => 'Option',
+            },
+            desc => {
+                _so => 3,
+                title => 'Description',
+            },
+        },
+    };
+
+    my @rows;
+
+    my $optionlist = $self->{metacache}->{$metasettings->{src}}->{optionsbyclass}->{$metasettings->{itemid}};
+
+    my $td = $self->{metacache}->{$metasettings->{src}}->{options};
+
+#    print STDERR "\n\nPREF for $metasettings->{itemid}\n\n";
+
+    foreach my $structureid (sort { ($td->{$a}->{description} or $a) cmp
+                                        ($td->{$b}->{description} or $b) } keys %{$optionlist})
+    {
+        my @row;
+
+        push(@row,
+             ((exists($metasettings->{idprefix}) &&
+               defined($metasettings->{idprefix})) ? 
+              $self->xml_tag('link',
+                             {
+                                 linkend => 
+                                     $self->merge_docbook_link($metasettings->{idprefix},
+                                                               $structureid),
+                             },
+                             $self->xml_literal($structureid)) : $self->xml_literal($structureid)
+              ),
+             $td->{$structureid}->{description});
+
+        push @rows,\@row;
+        next;
+ 
+       print <<EOF;
+TODO: Fill out this section
+  <section id="config-couchdb-options_attachments_$structureid">
+
+    <title><literal>$structureid</literal> Option</title>
+
+    <para>
+      Some...
+    </para>
+
+    <para role="meta" id="table-couchdb-config-options-attachments-$structureid-detail">
+      <remark role="title"><literal>$structureid</literal></remark>
+      <remark role="type" condition="config"/>
+      <remark role="src" condition="couchdb"/>
+      <remark role="output" condition="itemtable"/>
+      <remark role="version" condition="1.0"/>
+      <remark role="itemid" condition="$structureid"/>
+    </para>
+
+  </section>
+EOF
+    }
+    $self->generate_metadoc_table($tabledef,\@rows);
+#    print STDERR "\n\n";
+}
+
+sub itemtable
+{
+    my ($self,$metasettings) = @_;
+
+    my $baseitem = $self->{metacache}->{$metasettings->{src}}->{$metasettings->{itemid}};
+}
+
+sub load_metasrc
+{
+    my ($self,$metasettings) = @_;
+
+    my $fileref = sprintf('%s/%s/%s.xml',
+                          $self->{global_opts}->{metaroot},
+                          $self->{meta_opts}->{basedir},
+                          $metasettings->{src},
+        );
+
+    return if (exists($self->{filecache}->{$fileref}));
+
+    croak("Can't find $fileref")
+        unless(-f $fileref);
+
+    my $metafile = $self->slurp_file($fileref);
+
+    my $metaparser = CouchDocs::Config::Parser->new();
+    eval {
+      XML::Parser::PerlSAX->new->parse(Source => {String => $metafile,},
+                                       Handler => $metaparser,
+
+          );
+    };
+
+    $self->{filecache}->{$fileref} = 1;
+    $self->{dependencies}->{$fileref} = 1;
+    $self->{dependencies}->{$self->resolve_module_path('CouchDocs::Config::Parser')} = 1;
+    $self->{metacache}->{$metasettings->{src}} = $metaparser->{data};
+}
+
+1;

http://git-wip-us.apache.org/repos/asf/couchdb/blob/1a82fd7c/share/docs/DocKit/bin/CouchDocs/Config/Parser.pm
----------------------------------------------------------------------
diff --git a/share/docs/DocKit/bin/CouchDocs/Config/Parser.pm b/share/docs/DocKit/bin/CouchDocs/Config/Parser.pm
new file mode 100644
index 0000000..2dbbeec
--- /dev/null
+++ b/share/docs/DocKit/bin/CouchDocs/Config/Parser.pm
@@ -0,0 +1,101 @@
+package CouchDocs::Config::Parser;
+
+use strict;
+use warnings;
+use Data::Dumper;
+use Carp;
+
+use vars qw/@ISA/;
+@ISA = ('CouchDocs');
+
+sub new
+{
+    my ($self) = @_;
+
+    my $class = ref($self) || $self;
+
+    return bless {
+        _modulename => 'Config::Parser',
+        data => {},
+        captext => 0,
+        currenttext => '',
+        currentparent => '',
+    },$class;
+}
+
+sub start_element
+{
+    my ($self,$element) = @_;
+
+    if ($element->{Name} eq 'configclass')
+    {
+        $self->{data}->{classes}->{$element->{Attributes}->{id}} = {
+            description => ''
+        };
+
+        $self->setbyattrib(\$self->{data}->{classes}->{$element->{Attributes}->{id}},
+                           $element,
+                           [qw/iv ov dv rv/]);
+        $self->{currentid} = $element->{Attributes}->{id};
+        $self->{currentdescparent} = 'classes';
+    }
+    elsif ($element->{Name} eq 'configopt')
+    {
+        $self->{currentid} = $element->{Attributes}->{id};
+
+        $self->{data}->{options}->{$self->{currentid}} = {
+            class => $element->{Attributes}->{class},
+            id => $self->{currentid},
+            description => '',
+            values => [],
+        };
+
+        $self->setbyattrib(\$self->{data}->{options}->{$self->{currentid}},
+                           $element,
+                           [qw/iv ov dv rv/]);
+
+        $self->{data}->{optionsbyclass}->{$element->{Attributes}->{class}}->{$self->{currentid}} = 1;
+
+        $self->{currentdescparent} = 'options';
+    }
+    elsif (($element->{Name} eq 'description')
+           )
+    {
+        $self->{captext} = 1;
+    }
+    elsif (($element->{Name} eq 'valuespec')
+        )
+    {
+        
+    }
+}
+
+# Most of the validation should occur here, before the info
+# is stored and written to the final structure
+
+sub end_element
+{
+    my ($self,$element) = @_;
+
+    if ($element->{Name} eq 'description')
+    {
+        $self->{data}->{$self->{currentdescparent}}->{$self->{currentid}}->{description} = $self->{currenttext};
+
+        $self->{currenttext} = '';
+        $self->{captext} = 0;
+    }
+    elsif ($element->{Name} eq 'default')
+    {
+        $self->{currentvaluespec}->{default} = $self->{currenttext};
+        $self->{currenttext} = '';
+        $self->{captext} = 0;
+    }
+    elsif (($element->{Name} eq 'structure') ||
+           ($element->{Name} eq 'substructure')
+           )
+    {
+        confess("Cannot have an empty description") unless(exists($self->{data}->{$self->{currentid}}->{description}));
+    }
+}         
+
+1;

http://git-wip-us.apache.org/repos/asf/couchdb/blob/1a82fd7c/share/docs/DocKit/bin/CouchDocs/DBCheck.pm
----------------------------------------------------------------------
diff --git a/share/docs/DocKit/bin/CouchDocs/DBCheck.pm b/share/docs/DocKit/bin/CouchDocs/DBCheck.pm
new file mode 100644
index 0000000..f884c82
--- /dev/null
+++ b/share/docs/DocKit/bin/CouchDocs/DBCheck.pm
@@ -0,0 +1,146 @@
+package CouchDocs::DBCheck;
+
+use strict;
+use warnings;
+
+use LWP::UserAgent;
+use Data::Dumper;
+
+sub new
+{
+    my ($self,$opts) = @_;
+
+    my $class = ref($self) || $self;
+
+    return bless {
+        options => $opts,
+        _modulename => 'CouchDocs::DBCheck',
+        metacache => {},
+        filecache => {},
+    };
+}
+
+sub check_link
+{
+    my ($url) = @_;
+
+    my $ua = LWP::UserAgent->new;
+
+    my $trycount = 0;
+    my $success = 0;
+    my $retstring  = "";
+
+    while ($trycount <= 5)
+    {
+        my $response = $ua->head($url);
+
+        if ($response->is_error())
+        {
+            if (($response->{'_rc'} >= 301) and
+                 ($response->{'_rc'} <= 303))
+            {
+                $retstring = sprintf(' should be redirected to %s',$response->status_line());
+                $success = 0;
+                last;
+            }
+            if (($response->{_rc} >= 400) &&
+                ($response->{_rc} <= 499))
+            {
+                $retstring = sprintf(' fails with %s',$response->status_line());
+                $success = 0;
+                last;
+            }
+        }
+        else
+        {
+            $success = 1;
+            last;
+        }
+        $trycount++;
+    }
+    return($success,$retstring);
+}
+
+sub start_element
+{
+    my ($self, $element) = @_;
+
+    if ($self->{options}->{checkulink})
+    {
+        if  ($element->{Name} eq 'ulink' &&
+             $element->{Attributes}->{'url'} !~ m/^https/ &&
+             $element->{Attributes}->{'url'} !~ m/^mailto/)
+        {
+            if (!exists($self->{triedulinks}->{$element->{Attributes}->{'url'}}))
+            {
+                $self->{triedulinks}->{$element->{Attributes}->{'url'}} = 1;
+
+                my ($success,$errstring) = check_link($element->{Attributes}->{'url'});
+
+                if ($success == 0)
+                {
+                    push(@{$self->{issuelist}},
+                         {'parentid' => $self->{currsection},
+                          'type' => 'ulink',
+                          'class' => 'warning',
+                          'base' => $self->{currentbase},
+                          'text' => join('',"URL link failure ",
+                                         $element->{Attributes}->{url} || 'Not specified',
+                                         $errstring,
+                                         "\n")});
+                }
+            }
+        }
+    }
+
+    if ($element->{Name} =~ m/^(appendix|article|book|part|chapter|example|preface|refentry|refsection|section)$/i)
+    {
+        if (exists($element->{Attributes}->{id}))
+        {
+            push(@{$self->{sectionmap}},$element->{Attributes}->{id});
+            $self->{currsection} = $element->{Attributes}->{id};
+            if (exists($self->{sectionids}->{$element->{Attributes}->{id}}))
+            {
+                if (!exists($self->{checkskip}->{'idmissing'}))
+                {
+                    push(@{$self->{issuelist}},
+                         {'parentid' => $self->{currsection},
+                          'type' => 'idmissing',
+                          'class' => 'todo',
+                          'text' => "Found a duplicate reference ID ($element->{Attributes}->{id})\n"});
+                }
+            }
+            else
+            {
+                $self->{sectionids}->{$element->{Attributes}->{id}} = 0;
+            }
+            $self->{sectionids}->{$element->{Attributes}->{id}}++;
+        }
+        else
+        {
+            if (!exists($self->{checkskip}->{'idmissing'}))
+            {
+                push(@{$self->{issuelist}},
+                     {'parentid' => $self->{currsection},
+                      'type' => 'idmissing',
+                      'class' => 'todo',
+                      'text' => "Found a $element->{Name} without an ID!\n"});
+            }
+        }
+    }
+
+}
+
+sub end_element
+{
+    my ($self, $element) = @_;
+
+}
+
+sub characters
+{
+    my ($self, $element) = @_;
+
+}
+
+1;

http://git-wip-us.apache.org/repos/asf/couchdb/blob/1a82fd7c/share/docs/DocKit/bin/CouchDocs/JSON.pm
----------------------------------------------------------------------
diff --git a/share/docs/DocKit/bin/CouchDocs/JSON.pm b/share/docs/DocKit/bin/CouchDocs/JSON.pm
new file mode 100644
index 0000000..e05cfc7
--- /dev/null
+++ b/share/docs/DocKit/bin/CouchDocs/JSON.pm
@@ -0,0 +1,363 @@
+package CouchDocs::JSON;
+
+use strict;
+use warnings;
+use Carp;
+use XML::Parser::PerlSAX;
+
+use Data::Dumper;
+
+use vars qw/@ISA/;
+@ISA = ('CouchDocs');
+
+use CouchDocs::JSON::Parser;
+
+sub new
+{
+    my ($self) = @_;
+
+    my $class = ref($self) || $self;
+
+    return bless {
+        _modulename => 'CouchDocs::JSON',
+        meta_opts => {
+            basedir => 'json',
+        },
+        metacache => {},
+        filecache => {},
+    };
+}
+
+sub parse
+{
+    my ($self,$metasettings) = @_;
+
+    my $retval = {text => '',
+                  _files => ''};
+
+    $self->load_metasrc($metasettings);
+
+    $self->metatext_empty();
+
+    if ($metasettings->{output} eq 'summarytable')
+    {
+        $self->summarytable($metasettings);
+    }
+    if ($metasettings->{output} eq 'alltables')
+    {
+        $self->alltables($metasettings);
+    }
+    if ($metasettings->{output} eq 'itemtable')
+    {
+        $self->itemtable($metasettings);
+    }
+    if ($metasettings->{output} eq 'itemsample')
+    {
+        $self->itemsample($metasettings);
+    }
+
+    $retval->{dependencies} = $self->{dependencies};
+    $retval->{text} = $self->metatext_asstring();
+
+    return $retval;
+}
+
+sub validate
+{
+    my ($self,$metafile) = @_;
+
+    $self->load_metasrc({src => $metafile});
+}
+
+sub alltables
+{
+    my ($self,$metasettings) = @_;
+
+    my @tables;
+
+    my $td = $self->{metacache}->{$metasettings->{src}};
+    $self->metatext_empty();
+
+    foreach my $structureid (sort { ($td->{$a}->{description} or $a) cmp
+                                        ($td->{$b}->{description} or $b) } keys %{$td})
+    {
+        next if ($td->{$structureid}->{sub} == 1);
+        $self->metatext_empty();
+
+        # Only top-level structures are listed
+
+        my $tablesettings = $self->deep_copy($metasettings);
+        $tablesettings->{id} = $self->merge_docbook_link($metasettings->{idprefix},
+                                                         $structureid);
+        $tablesettings->{itemid} = $structureid;
+        $self->itemtable($tablesettings);
+        push @tables,$self->metatext_asstring();
+    }
+    $self->metatext_empty();
+    $self->metatext_print(join('',@tables));
+}
+
+sub summarytable
+{
+    my ($self,$metasettings) = @_;
+
+    my $tabledef = {
+        title => $metasettings->{title},
+        id => $metasettings->{id} || undef,
+        type => 'table',
+        collist => {
+            desc => {
+                _so => 3,
+                title => 'Description',
+            },
+        },
+    };
+
+    my @rows;
+
+    my $td = $self->{metacache}->{$metasettings->{src}};
+
+    foreach my $structureid (sort { ($td->{$a}->{description} or $a) cmp
+                                        ($td->{$b}->{description} or $b) } keys %{$td})
+    {
+        # Only top-level structures are listed
+        next if ($td->{$structureid}->{sub} == 1);
+
+        my @row;
+
+        push @row,$self->xml_tag('link',
+                                 {
+                                     linkend => 
+                                         $self->merge_docbook_link($metasettings->{idprefix},
+                                                                   $structureid),
+                                 },
+                                 $td->{$structureid}->{description});
+        
+        push @rows,\@row;
+    }
+    $self->generate_metadoc_table($tabledef,\@rows); 
+}
+
+sub itemtable
+{
+    my ($self,$metasettings) = @_;
+
+    my $baseitem = $self->{metacache}->{$metasettings->{src}}->{$metasettings->{itemid}};
+
+    if (!defined($baseitem))
+    {
+        $self->meta_error("Cannot find definition for $metasettings->{itemid}\n");
+    }
+
+# Work through the entire structure and calculate the width
+# We can go back and add in the table column headings later
+
+    my @rows;
+
+    push @rows, $self->itemtable_row(0,
+                                     0,
+                                     $baseitem,
+                                     $metasettings,
+        );
+
+#    my $width = 0;
+#    foreach my $row (@rows)
+#    {
+#        $width = scalar(@{$row})
+#            if (scalar(@{$row}) > $width);
+#    }
+
+    my $tabledef = {
+        title => $metasettings->{title} || $baseitem->{description},
+        id => $metasettings->{id} || undef,
+        nocolhead => 1,
+        type => 'table',
+        class => 'jsonstructure',
+        collist => {
+            item => { 
+                _so => 1,
+                title => 'Field',
+                width => 30,
+            },
+            desc => {
+                _so => 99,
+                title => 'Description',
+                width => 70,
+            },
+        },
+    };
+
+# Set the individual columns for the field nest
+
+#    my $colwidth = int(30/$width);
+
+#    foreach my $id (0..($width-1))
+#    {
+#        $tabledef->{collist}->{sprintf('item-%d',$id)} = {
+#            _so => $id,
+#            title => sprintf('item-%d',$id),
+#            width => $colwidth,
+#        };
+#    }
+
+#     $tabledef->{collist}->{lastitem} = {
+#             _so => 98,
+#             title => 'lastitem',
+#             width => $colwidth,
+#     };
+
+    unshift(@rows,
+            [
+             {
+                 attr => { 
+#                     namest => sprintf('item-%d',0),
+#                     nameend => 'lastitem',
+                 },
+                 content => $self->xml_tag('emphasis',{role => 'bold'},'Field')
+             },
+             $self->xml_tag('emphasis',{role => 'bold'},'Description')
+            ]
+        );
+
+    $self->generate_metadoc_table($tabledef,\@rows); 
+}
+
+sub itemtable_row
+{
+    my ($self,$depth,$parentdepth,$item,$metasettings) = @_;
+
+    my @rows;
+
+    if (exists($item->{merge}))
+    {
+        foreach my $include (sort keys %{$item->{merge}})
+        {
+            push @rows,$self->itemtable_row($depth,
+                                            $depth,
+                                            $self->{metacache}->{$metasettings->{src}}->{$include},
+                                            $metasettings);
+        }
+    }
+
+    foreach my $field (sort keys %{$item->{fields}})
+    {
+        my @row;
+
+        my $type = '';
+
+        if (exists($item->{fields}->{$field}->{type}) &&
+            ($item->{fields}->{$field}->{type} eq 'array'))
+        {
+            $type = $self->xml_tag('literal','[array]');
+        }
+
+        my $optional = '';
+        
+        if (exists($item->{fields}->{$field}->{optional}) &&
+            ($item->{fields}->{$field}->{optional} eq 'yes'))
+        {
+            $type = '(optional)';
+        }
+
+# Some sections removed temporarily
+
+#         push(@row,
+#              {
+#                  attr => {
+#                      namest => sprintf('item-%d',0),
+#                      nameend => sprintf('item-%d',$parentdepth),
+#                  },
+#                  content => '',
+#              })
+#             if ($depth > 0);
+
+        push(@row,
+             {
+                 attr => {
+#                     namest => sprintf('item-%d',$depth),
+#                     nameend => sprintf('lastitem',$parentdepth),
+                 },
+                 content => sprintf('%s%s %s %s',
+                                    ('&nbsp;' x ($depth*8)),
+                                    $self->xml_tag('literal',$field),
+                                    $type,
+                                    $optional),
+             });
+
+        if (exists($item->{fields}->{$field}->{merge}))
+        {
+            foreach my $include (sort keys %{$item->{fields}->{$field}->{merge}})
+            {
+                push(@row,
+                             $self->{metacache}->{$metasettings->{src}}->{$include}->{description},
+                    );
+                push @rows,\@row;
+                push @rows,$self->itemtable_row(($depth+1),
+                                                $depth,
+                                                $self->{metacache}->{$metasettings->{src}}->{$include},
+                                                $metasettings);
+            }
+        }
+        else
+        {
+            push @row,$item->{fields}->{$field}->{description};
+            push @rows,\@row;
+        }
+    }
+
+    return(@rows);
+}
+
+
+sub itemsample
+{
+    my ($self,$metasettings) = @_;
+
+    my $baseitem = $self->{metacache}->{$metasettings->{src}}->{$metasettings->{itemid}};
+
+# All JSON objects are exactly that, objects
+
+    
+
+}
+
+sub itemsample_builder
+{
+    my ($self,$itemid,$metasettings) = @_;
+
+    
+
+}
+
+sub load_metasrc
+{
+    my ($self,$metasettings) = @_;
+
+    my $fileref = sprintf('%s/%s/%s.xml',
+                          $self->{global_opts}->{metaroot},
+                          $self->{meta_opts}->{basedir},
+                          $metasettings->{src},
+        );
+
+    return if (exists($self->{filecache}->{$fileref}));
+
+    croak("Can't find $fileref")
+        unless(-f $fileref);
+
+    my $metafile = $self->slurp_file($fileref);
+
+    my $metaparser = CouchDocs::JSON::Parser->new();
+    eval {
+      XML::Parser::PerlSAX->new->parse(Source => {String => $metafile,},
+                                       Handler => $metaparser,
+
+          );
+    };
+
+    $self->{filecache}->{$fileref} = 1;
+    $self->{dependencies}->{$fileref} = 1;
+    $self->{dependencies}->{$self->resolve_module_path('CouchDocs::JSON::Parser')} = 1;
+    $self->{metacache}->{$metasettings->{src}} = $metaparser->{data};
+}
+
+1;

http://git-wip-us.apache.org/repos/asf/couchdb/blob/1a82fd7c/share/docs/DocKit/bin/CouchDocs/JSON/Parser.pm
----------------------------------------------------------------------
diff --git a/share/docs/DocKit/bin/CouchDocs/JSON/Parser.pm b/share/docs/DocKit/bin/CouchDocs/JSON/Parser.pm
new file mode 100644
index 0000000..52494a7
--- /dev/null
+++ b/share/docs/DocKit/bin/CouchDocs/JSON/Parser.pm
@@ -0,0 +1,127 @@
+package CouchDocs::JSON::Parser;
+
+use strict;
+use warnings;
+use Data::Dumper;
+use Carp;
+
+use vars qw/@ISA/;
+@ISA = ('CouchDocs');
+
+sub new
+{
+    my ($self) = @_;
+
+    my $class = ref($self) || $self;
+
+    return bless {
+        _modulename => 'JSON::Parser',
+        data => {},
+        captext => 0,
+        currenttext => '',
+        currentparent => '',
+    },$class;
+}
+
+sub start_element
+{
+    my ($self,$element) = @_;
+
+    if (($element->{Name} eq 'structure') ||
+        ($element->{Name} eq 'substructure'))
+    {
+        if (!exists($element->{Attributes}->{id}))
+        {
+            croak("id is a required attribute to json");
+        }
+        $self->{currentid} = $element->{Attributes}->{id};
+        if (exists($self->{data}->{$self->{currentid}}))
+        {
+            croak("ID $self->{currentid} already exists");
+        }
+        $self->{data}->{$self->{currentid}} = {
+            id => $self->{currentid},
+            sub => ($element->{Name} eq 'substructure' ? 1 : 0),
+        };
+        $self->{currentparent} = 'structure';
+        $self->setbyattrib(\$self->{data}->{$self->{currentid}},$element,[qw/iv ov dv rv xrefto/]);
+        $self->validate_object_version("JSON $self->{currentid}",
+                                       $self->{data}->{$self->{currentid}});
+    }
+    elsif ($element->{Name} eq 'field')
+    {
+        $self->{currentfield} = {};
+        $self->{currentparent} = 'field';
+        if (!exists($element->{Attributes}->{id}))
+        {
+            croak("id is a required attribute to field");
+        }
+        $self->{currentfieldid} = $element->{Attributes}->{id};
+        $self->setbyattrib(\$self->{currentfield},
+                           $element,
+                           [qw/id optional type iv ov dv rv/]);
+        $self->validate_object_version("JSON field $self->{currentfieldid}",
+                                       $self->{currentfield});
+    }
+    elsif (($element->{Name} eq 'description') ||
+           ($element->{Name} eq 'fielddesc')
+           )
+    {
+        $self->{captext} = 1;
+    }
+    elsif (($element->{Name} eq 'seealso') ||
+           ($element->{Name} eq 'merge')
+        )
+    {
+        if (!exists($element->{Attributes}->{id}))
+        {
+            croak("id is a required attribute for references");
+        }
+        if ($self->{currentparent} eq 'structure')
+        {
+            $self->{data}->{$self->{currentid}}->{$element->{Name}}->{$element->{Attributes}->{id}} = 1;
+        }
+        else
+        {
+            $self->{currentfield}->{$element->{Name}}->{$element->{Attributes}->{id}} = 1;
+        }
+    }
+}
+
+# Most of the validation should occur here, before the info
+# is stored and written to the final structure
+
+sub end_element
+{
+    my ($self,$element) = @_;
+
+    if ($element->{Name} eq 'field')
+    {
+#        die("Description for field $self->{currentfieldid} is required") 
+#            unless(length($self->{currentfield}->{description}) > 0);
+        $self->{data}->{$self->{currentid}}->{fields}->{$self->{currentfieldid}} = $self->{currentfield};
+    }
+    elsif ($element->{Name} eq 'description')
+    {
+        $self->{data}->{$self->{currentid}}->{description} = $self->{currenttext};
+#        die("Cannot have an empty description") 
+#            unless ($self->{data}->{$self->{currentid}}->{description} =~ m/[a-z0-9]/i);
+
+        $self->{currenttext} = '';
+        $self->{captext} = 0;
+    }
+    elsif ($element->{Name} eq 'fielddesc')
+    {
+        $self->{currentfield}->{description} = $self->{currenttext};
+        $self->{currenttext} = '';
+        $self->{captext} = 0;
+    }
+    elsif (($element->{Name} eq 'structure') ||
+           ($element->{Name} eq 'substructure')
+           )
+    {
+        confess("Cannot have an empty description") unless(exists($self->{data}->{$self->{currentid}}->{description}));
+    }
+}         
+
+1;

http://git-wip-us.apache.org/repos/asf/couchdb/blob/1a82fd7c/share/docs/DocKit/bin/CouchDocs/URLAPI.pm
----------------------------------------------------------------------
diff --git a/share/docs/DocKit/bin/CouchDocs/URLAPI.pm b/share/docs/DocKit/bin/CouchDocs/URLAPI.pm
new file mode 100644
index 0000000..79ea37e
--- /dev/null
+++ b/share/docs/DocKit/bin/CouchDocs/URLAPI.pm
@@ -0,0 +1,461 @@
+package CouchDocs::URLAPI;
+
+use strict;
+use warnings;
+use Carp;
+use XML::Parser::PerlSAX;
+
+use vars qw/@ISA/;
+@ISA = ('CouchDocs');
+
+use CouchDocs::URLAPI::Parser;
+
+sub new
+{
+    my ($self) = @_;
+
+    my $class = ref($self) || $self;
+
+    return bless {
+        _modulename => 'CouchDocs::URLAPI',
+        meta_opts => {
+            basedir => 'urlapi',
+        },
+        metacache => {},
+        filecache => {},
+    };
+}
+
+sub parse
+{
+    my ($self,$metasettings) = @_;
+
+    my $retval = {text => '',
+                  _files => ''};
+
+    $self->load_metasrc($metasettings);
+
+    $self->metatext_empty();
+
+    if ($metasettings->{output} eq 'summarytable')
+    {
+        $self->summarytable($metasettings);
+    }
+    if ($metasettings->{output} eq 'accesstable')
+    {
+        $self->accesstable($metasettings);
+    }
+
+    $retval->{dependencies} = $self->{dependencies};
+    $retval->{text} = $self->metatext_asstring();
+
+    return $retval;
+}
+
+sub validate
+{
+    my ($self,$metafile) = @_;
+        
+    $self->load_metasrc({src => $metafile});
+}
+
+sub summarytable
+{
+    my ($self,$metasettings) = @_;
+
+    my $tabledef = {
+        title => $metasettings->{title},
+        id => $metasettings->{id} || undef,
+        type => 'table',
+        collist => {
+            method => {
+                _so => 1,
+                title => 'Method',
+            },
+            path => {
+                _so => 2,
+                title => 'Path',
+            },
+            desc => {
+                _so => 3,
+                title => 'Description',
+            },
+        },
+    };
+
+    my $td = $self->{metacache}->{$metasettings->{src}}->{paths};
+
+    my @rows;
+
+    foreach my $id (sort { $td->{$a}->{path} cmp
+                              $td->{$b}->{path} }
+                    keys %{$td})
+    {
+        next unless($self->meta_object_filter($metasettings,
+                                              $td->{$id}));
+
+        foreach my $access (sort { $a->{_so} <=> $b->{_so} } 
+                            @{$td->{$id}->{access}})
+        {
+            next unless($self->meta_object_filter($metasettings,
+                                                  $access));
+            my @row;
+
+            push @row,sprintf('<literal>%s</literal>',$access->{method});
+            push @row,sprintf('<literal>%s</literal>',$td->{$id}->{path});
+            if (                   
+                exists($td->{$id}->{xrefto})
+                )
+            {
+                push(@row,sprintf('<link linkend="%s">%s</link>',
+                                  $self->merge_docbook_link($metasettings->{idprefix},
+                                                            $td->{$id}->{xrefto},
+                                                            lc($access->{method}),
+                                                     ),
+                                  $access->{description}));
+            }
+            push @rows,\@row;
+        }
+    }
+
+    $self->generate_metadoc_table($tabledef,\@rows);
+}
+
+sub accesstable
+{
+    my ($self,$metasettings) = @_;
+
+    my $entry = undef;
+
+    my $urlbase = $self->{metacache}->{$metasettings->{src}}->{paths}->{$metasettings->{itemid}};
+
+    if (!defined($urlbase))
+    {
+        $self->meta_error("URL item $metasettings->{itemid} not found");
+    }
+
+    foreach my $find (@{$urlbase->{access}})
+    {
+        $entry = $find
+            if ($find->{method} eq $metasettings->{method});
+    }
+
+    use Data::Dumper;
+    if (!defined($entry))
+    {
+       confess "$metasettings->{method} in $metasettings->{itemid} not found";
+    }
+
+    my $tabledef = {
+        title => $metasettings->{title} || sprintf('URL API %s %s',$entry->{method},$urlbase->{path}),
+        id => $metasettings->{id} || undef,
+        nocolhead => 1,
+        type => 'informaltable',
+        collist => {
+            field => {
+                _so => 1,
+            },
+            info => {
+                _so => 2,
+            },
+            addinfo => {
+                _so => 3,
+            },
+        },
+    };
+
+    my @rows;
+
+    if (!exists($metasettings->{opt_queryargs_only}) ||
+        $metasettings->{opt_queryargs_only} eq "no")
+    {
+        push(@rows,
+             $self->accesstable_row('Method',
+                                    {
+                                        attr => {
+                                            namest => 'info',
+                                            nameend => 'addinfo',
+                                        },
+                                        content => sprintf('<literal>%s %s</literal>',
+                                                           $entry->{method},
+                                                           $urlbase->{path},
+                                            ),
+                                    }
+             ),
+             $self->accesstable_row('Request',
+                                    {
+                                        attr => {
+                                            namest => 'info',
+                                            nameend => 'addinfo',
+                                        },
+                                        content => $entry->{request},
+                                    }
+             ),
+             $self->accesstable_row('Response',
+                                    {
+                                        attr => {
+                                            namest => 'info',
+                                            nameend => 'addinfo',
+                                        },
+                                        content => $entry->{response},
+                                    }
+             ),
+             $self->accesstable_row('Admin Privileges Required',
+                                    {
+                                        attr => {
+                                            namest => 'info',
+                                            nameend => 'addinfo',
+                                        },
+                                        content => $entry->{admin} || 'no',
+                                    }
+             ),
+            );
+    }
+
+    if (exists($entry->{qa}) &&
+        scalar @{$entry->{qa}})
+    {
+# First we work out if there is a list of valid QAs for the specified version        
+        my @qas;
+
+        foreach my $qa (sort { ($a->{iv} || $a->{ov} || 1.0) <=> 
+                                   ($b->{iv} || $b->{ov} || 9.99.99) }
+                        @{$entry->{qa}})
+        {
+            next unless($self->meta_object_filter($metasettings,$qa));
+            push @qas,$qa;
+        }
+
+# TODO Need to display version information 
+
+        my $rows = 0;
+        foreach my $qa (@qas)
+        {
+            my $header = 'Query Arguments';
+
+# Proceed through the list of arguments
+
+            foreach my $arg (sort { $a->{name} cmp $b->{name} } @{$qa->{args}})
+            {
+                $header = undef if ($rows > 0);
+
+                if (!exists($metasettings->{opt_queryargs_only}) ||
+                    $metasettings->{opt_queryargs_only} eq "no")
+                {
+                    push(@rows,[
+                             (defined($header) ? $self->xml_tag('emphasis',{role => 'bold'},$header) : ''),
+                             $self->xml_tag('emphasis',{role => 'bold'},'Argument'),
+                             $self->xml_tag('literal',$arg->{name}),
+                         ]);
+                }
+                else
+                {
+                    push(@rows,[
+                             $self->xml_tag('emphasis',{role => 'bold'},'Argument'),
+                             $self->xml_tag('literal',$arg->{name}),
+                         ]);
+                }                    
+
+                $rows++;
+
+                foreach my $field (qw/description opt type default min max qty/)
+                {
+                    my $row;
+
+                    $row = $self->accesstable_subrow($arg,$field);
+
+                    push @rows,$row if (defined($row));
+                }
+
+                if (exists($arg->{values}) && 
+                    scalar @{$arg->{values}})
+                {
+                    push(@rows,[
+                             '',
+                             $self->xml_tag('emphasis',{role => 'bold'},'Supported Values'),
+                         ]);
+                    foreach my $value (sort { $a->{value} cmp $b->{value} } @{$arg->{values}})
+                    {
+                        next unless($self->meta_object_filter($metasettings,$value));
+                        my $qaverspec = $self->describe_object_version_astext($value);
+                        push(@rows,[
+                                 '',
+                                 $self->xml_tag('literal',$value->{value}),
+                                 sprintf('%s%s',
+                                         $value->{description},
+                                         (defined($qaverspec) ? sprintf(' (%s)',$qaverspec) : ''),
+                                 ),
+                             ]);
+                    }
+                }                        
+# Insert a blank row 
+                push @rows,['','',''];
+            }
+        }
+    }
+
+    if (($rows[-1]->[0] eq '') &&
+        ($rows[-1]->[1] eq '') &&
+        ($rows[-1]->[2] eq '')
+    )
+    {
+        pop @rows;
+    }
+
+    if (exists($entry->{httpheaders}) &&
+        scalar @{$entry->{httpheaders}})
+    {
+# First we work out if there is a list of valid HTTP Headers for the specified version        
+        my @hhs;
+
+        foreach my $hh (sort { ($a->{iv} || $a->{ov} || 1.0) <=> 
+                                   ($b->{iv} || $b->{ov} || 9.99.99) }
+                        @{$entry->{httpheaders}})
+        {
+            next unless($self->meta_object_filter($metasettings,$hh));
+            push @hhs,$hh;
+        }
+
+# TODO Need to display version information 
+
+        my $rows = 0;
+        foreach my $httpheader (@hhs)
+        {
+            my $header = 'HTTP Headers';
+
+# Proceed through the list of arguments
+
+            foreach my $arg (sort { $a->{name} cmp $b->{name} } @{$httpheader->{headers}})
+            {
+                $header = undef if ($rows > 0);
+                push(@rows,[
+                         (defined($header) ? $self->xml_tag('emphasis',{role => 'bold'},$header) : ''),
+                         $self->xml_tag('emphasis',{role => 'bold'},'Header'),
+                         $self->xml_tag('literal',$arg->{name}),
+                     ]);
+                $rows++;
+
+                foreach my $field (qw/description opt type default min max qty/)
+                {
+                    my $row = 
+                        $self->accesstable_subrow($arg,$field);
+                    push @rows,$row if (defined($row));
+                }
+
+                if (scalar keys %{$arg->{values}})
+                {
+                    push(@rows,[
+                             '',
+                             $self->xml_tag('emphasis',{role => 'bold'},'Supported Values'),
+                         ]);
+                    foreach my $value (sort { $a cmp $b } keys %{$arg->{values}})
+                    {
+                        push(@rows,[
+                                 '',
+                                 $self->xml_tag('literal',$value),
+                                 $arg->{values}->{$value},
+                             ]);
+                    }
+                }                        
+# Insert a blank row 
+                push @rows,['','',''];
+            }
+        }
+    }
+
+    if (scalar keys %{$entry->{returncodes}} > 0)
+    {
+        my $rows = 0;
+
+        foreach my $returncode (sort keys %{$entry->{returncodes}})
+        {
+            if ($rows == 0)
+            {
+                push(@rows,[{attr => { namest => 'field',
+                                       nameend => 'addinfo',},
+                             content => $self->xml_tag('emphasis',{role => 'bold'},'Return Codes')},]);
+            }
+
+            push(@rows,[$returncode,
+                        {attr => { namest => 'info',
+                                   nameend => 'addinfo',},
+                         content => $entry->{returncodes}->{$returncode}},]);
+            
+            $rows++;
+        }
+    }
+
+    $self->generate_metadoc_table($tabledef,\@rows);
+}
+
+sub accesstable_subrow
+{
+    my ($self,$arg,$field) = @_;
+
+    my $fieldtitle = { 
+        'description' => 'Description',
+        'opt' => 'Optional',
+        'type' => 'Type',
+        'default' => 'Default',
+        'min' => 'Minimum',
+        'max' => 'Maximum',
+        'qty' => 'Quantity',
+    };
+
+    if (exists($arg->{$field}))
+    {
+        return([
+            '',
+            $self->xml_tag('emphasis',{role => 'bold'},$fieldtitle->{$field}),
+            $arg->{$field},
+               ]) 
+    }
+    return(undef);
+}
+
+sub accesstable_row
+{
+    my ($self,$title,@content) = @_;
+
+    my @row;
+
+    push(@row,
+         sprintf('<emphasis role="bold">%s</emphasis>',
+                 $title),
+         @content,
+         );
+    return(\@row);
+}
+
+sub load_metasrc
+{
+    my ($self,$metasettings) = @_;
+
+    my $fileref = sprintf('%s/%s/%s.xml',
+                          $self->{global_opts}->{metaroot},
+                          $self->{meta_opts}->{basedir},
+                          $metasettings->{src},
+        );
+
+    return if (exists($self->{filecache}->{$fileref}));
+
+    croak("Can't find $fileref") 
+        unless(-f $fileref);
+
+    my $metafile = $self->slurp_file($fileref);
+
+    my $metaparser = CouchDocs::URLAPI::Parser->new();
+    eval {
+      XML::Parser::PerlSAX->new->parse(Source => {String => $metafile,},
+                                       Handler => $metaparser,
+
+          );
+    };
+
+    $self->{filecache}->{$fileref} = 1;
+    $self->{dependencies}->{$fileref} = 1;
+    $self->{dependencies}->{$self->resolve_module_path('CouchDocs::URLAPI::Parser')} = 1;
+    $self->{metacache}->{$metasettings->{src}} = $metaparser->{data};
+}
+
+1;

http://git-wip-us.apache.org/repos/asf/couchdb/blob/1a82fd7c/share/docs/DocKit/bin/CouchDocs/URLAPI/Parser.pm
----------------------------------------------------------------------
diff --git a/share/docs/DocKit/bin/CouchDocs/URLAPI/Parser.pm b/share/docs/DocKit/bin/CouchDocs/URLAPI/Parser.pm
new file mode 100644
index 0000000..b504b20
--- /dev/null
+++ b/share/docs/DocKit/bin/CouchDocs/URLAPI/Parser.pm
@@ -0,0 +1,270 @@
+package CouchDocs::URLAPI::Parser;
+
+use strict;
+use warnings;
+use Data::Dumper;
+use Carp;
+use File::Basename;
+
+use vars qw/@ISA/;
+@ISA = ('CouchDocs');
+
+sub new
+{
+    my ($self) = @_;
+
+    my $class = ref($self) || $self;
+
+    return bless {
+        _modulename => 'URLAPI::Parser',
+# holds the data
+        data => { 
+            paths => {},
+            _defaultc => {},
+        },
+# tracks whether we are reading default return codes
+        indefaultrc => 0,
+# indicates whether to capture text or not
+        captext => 0,
+        currenttext => '',
+# Used to sort method names in the output
+        methodso => {
+            'GET' => 1,
+            'HEAD' => 2,
+            'PUT' => 4,
+            'POST' => 8,
+            'DELETE' => 16,
+            'COPY' => 32,
+        },
+    },$class;
+}
+
+sub start_element
+{
+    my ($self,$element) = @_;
+
+    if ($element->{Name} eq 'defaultreturncodes')
+    {
+        $self->{indefaultrc} = 1;
+    }
+
+    if ($element->{Name} eq 'urlapi')
+    {
+        if (!exists($element->{Attributes}->{id}))
+        {
+            croak("id is a required attributed to urlapi");
+        }
+        if (!exists($element->{Attributes}->{path}))
+        {
+            croak("path is a required attributed to urlapi");
+        }
+        $self->{currentid} = $element->{Attributes}->{id};
+        if (exists($self->{data}->{paths}->{$self->{currentid}}))
+        {
+            croak("ID $self->{currentid} already exists");
+        }
+        $self->{data}->{paths}->{$self->{currentid}} = {
+            id => $self->{currentid},
+        };
+
+        $self->setbyattrib(\$self->{data}->{paths}->{$self->{currentid}},$element,[qw/class path xrefto iv ov dv rv/]);
+        $self->validate_object_version("URLAPI path $self->{data}->{paths}->{$self->{currentid}}->{path}",
+                                       $self->{data}->{paths}->{$self->{currentid}});
+    }
+    elsif ($element->{Name} eq 'access')
+    {
+        $self->{currentaccess} = {
+            qa => [],
+        };
+
+        $self->setbyattrib(\$self->{currentaccess},$element,[qw/method xrefto iv ov dv rv admin/]);
+        if (!exists($self->{currentaccess}->{method}))
+        {
+            croak("Attribute method is required for an access entry");
+        }
+        $self->validate_object_version("URLAPI path $self->{data}->{paths}->{$self->{currentid}}->{path} method $self->{currentaccess}->{method}",
+                                       $self->{currentaccess});
+    }
+    elsif ($element->{Name} eq 'queryargs')
+    {
+        $self->{currentqa} = {
+            args => [],
+        };
+        $self->setbyattrib(\$self->{currentqa},$element,[qw/iv ov dv rv/]);
+        $self->validate_object_version("Query args in path $self->{data}->{paths}->{$self->{currentid}}->{path} method $self->{currentaccess}->{method}",
+                                       $self->{currentaccess});
+    }
+    elsif ($element->{Name} eq 'httpheaders')
+    {
+        $self->{currenthttpheaders} = {
+            headers => [],
+        };
+        $self->setbyattrib(\$self->{currenthttpheaders},$element,[qw/iv ov dv rv/]);
+    }
+    elsif (($element->{Name} eq 'description') ||
+           ($element->{Name} eq 'request') ||
+           ($element->{Name} eq 'response')
+           )
+    {
+        $self->{captext} = 1;
+    }
+    elsif ($element->{Name} eq 'returncode')
+    {
+        croak("Must specify the return code") unless(exists($element->{Attributes}->{code}) &&
+                                                      defined($element->{Attributes}->{code}));
+        $self->{currenterrorcode} = $element->{Attributes}->{code};
+        $self->{captext} = 1;
+    }
+    elsif ($element->{Name} eq 'arg')
+    {
+        $self->{currentarg} = {};
+        $self->meta_error("Must specify an argument name") unless(exists($element->{Attributes}->{name}) &&
+                                                                    defined($element->{Attributes}->{name}));
+        
+        $self->setbyattrib(\$self->{currentarg},$element,[qw/name type default min max opt qty/]);
+    }
+    elsif ($element->{Name} eq 'httpheader')
+    {
+        $self->{currenthttpheader} = {};
+        $self->meta_error("Must specify an http header name") unless(exists($element->{Attributes}->{name}) &&
+                                                                       defined($element->{Attributes}->{name}));
+
+        $self->setbyattrib(\$self->{currenthttpheader},$element,[qw/name type default min max opt qty/]);
+    }
+    elsif ($element->{Name} eq 'argdesc')
+    {
+        $self->{captext} = 1;
+    }
+    elsif ($element->{Name} eq 'httpheaderdesc')
+    {
+        $self->{captext} = 1;
+    }
+    elsif ($element->{Name} eq 'option')
+    {
+        croak("Must specify an value name") unless(exists($element->{Attributes}->{value}) &&
+                                                      defined($element->{Attributes}->{value}));
+        $self->{currentoption} = {
+            value => $element->{Attributes}->{value},
+        };
+        
+        $self->setbyattrib(\$self->{currentoption},$element,[qw/iv ov dv rv/]);
+        $self->{captext} = 1;
+    }
+}
+
+# Most of the validation should occur here, before the info
+# is stored and written to the final structure
+
+sub end_element
+{
+    my ($self,$element) = @_;
+
+    if ($element->{Name} eq 'defaultreturncodes')
+    {
+        $self->{indefaultrc} = 0;
+    }
+
+    if ($element->{Name} eq 'arg')
+    {
+        push(@{$self->{currentqa}->{args}}, $self->{currentarg});
+    }
+    if ($element->{Name} eq 'httpheader')
+    {
+        push(@{$self->{currenthttpheaders}->{headers}}, $self->{currenthttpheader});
+    }
+    elsif ($element->{Name} eq 'queryargs')
+    {
+        if (scalar @{$self->{currentqa}->{args}} < 1)
+        {
+            $self->meta_error("Cannot have a Query Args section with no arguments");
+        }
+        push(@{$self->{currentaccess}->{qa}},$self->{currentqa});
+    }
+    elsif ($element->{Name} eq 'httpheaders')
+    {
+        push(@{$self->{currentaccess}->{httpheaders}},$self->{currenthttpheaders});
+    }
+    elsif ($element->{Name} eq 'description')
+    {
+        $self->{currentaccess}->{description} = $self->{currenttext};
+        $self->reset_parser_charbuffer();    
+    }
+    elsif ($element->{Name} eq 'argdesc')
+    {
+        $self->{currentarg}->{description} = $self->{currenttext};
+        $self->reset_parser_charbuffer();    
+    }
+    elsif ($element->{Name} eq 'httpheaderdesc')
+    {
+        $self->{currenthttpheader}->{description} = $self->{currenttext};
+        $self->reset_parser_charbuffer();    
+    }
+    elsif ($element->{Name} eq 'option')
+    {
+        $self->meta_error('Option value must be filled') unless($self->{currenttext} =~ m/[a-z0-9]/i);
+        $self->{currentoption}->{description} = $self->{currenttext};
+        push @{$self->{currentarg}->{values}},$self->{currentoption};
+        $self->reset_parser_charbuffer();    
+    }
+    elsif ($element->{Name} eq 'request')
+    {
+        $self->{currentaccess}->{request} = $self->{currenttext};
+        $self->reset_parser_charbuffer();    
+    }
+    elsif ($element->{Name} eq 'response')
+    {
+        $self->{currentaccess}->{response} = $self->{currenttext};
+        $self->reset_parser_charbuffer();    
+    }
+    elsif ($element->{Name} eq 'returncode')
+    {
+        if ($self->{indefaultrc} == 1)
+        {
+            $self->meta_error('Returncode description must be filled') unless($self->{currenttext} =~ m/[a-z0-9]/i);
+            $self->{data}->{_defaultrc}->{$self->{currenterrorcode}} = $self->{currenttext};
+        }
+        else
+        {
+# If the return code is within the access definition, then a number without text imports the default
+# error code information
+            if ($self->{currenttext} =~ m/[a-z0-9]/i)
+            {
+                $self->{currentaccess}->{returncodes}->{$self->{currenterrorcode}} = $self->{currenttext};
+            }
+            else
+            {
+                if (exists($self->{data}->{_defaultrc}->{$self->{currenterrorcode}}))
+                {
+                    $self->{currentaccess}->{returncodes}->{$self->{currenterrorcode}} = 
+                        $self->{data}->{_defaultrc}->{$self->{currenterrorcode}};
+                }
+                else
+                {
+                    $self->meta_error("Default error code text for $self->{currenterrorcode} not found");
+                }
+            }
+        }
+        $self->reset_parser_charbuffer();
+    }
+    elsif ($element->{Name} eq 'access')
+    {
+        foreach my $tag (qw/description request response/)
+        {
+            $self->meta_error("Missing content for \u$tag for $self->{currentaccess}->{method}: $self->{data}->{paths}->{$self->{currentid}}->{path} tag")
+                unless (exists($self->{currentaccess}->{$tag}) && 
+                        $self->{currentaccess}->{$tag} =~ m/[a-zA-Z0-9]+/);
+        }
+
+        $self->{currentaccess}->{_so} = $self->{methodso}->{$self->{currentaccess}->{method}} || 65536;
+
+        push(@{$self->{data}->{paths}->{$self->{currentid}}->{access}},$self->{currentaccess});
+    }
+    elsif ($element->{Name} eq 'urlapi')
+    {
+        croak "Missing path for URLAPI for $self->{currentid}" 
+            if (!exists($self->{data}->{paths}->{$self->{currentid}}->{path}) || 
+                !defined($self->{data}->{paths}->{$self->{currentid}}->{path}));
+    }
+}         
+
+1;

http://git-wip-us.apache.org/repos/asf/couchdb/blob/1a82fd7c/share/docs/DocKit/bin/dateofdir.pl
----------------------------------------------------------------------
diff --git a/share/docs/DocKit/bin/dateofdir.pl b/share/docs/DocKit/bin/dateofdir.pl
new file mode 100755
index 0000000..3aad7c2
--- /dev/null
+++ b/share/docs/DocKit/bin/dateofdir.pl
@@ -0,0 +1,24 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+use File::Basename;
+use Carp;
+BEGIN {
+    use lib dirname($0);
+}
+use DateTime;
+
+my $maxdate = 0;
+
+foreach my $file (@ARGV)
+{
+    next if ($file =~ m{/common/});
+    my $filedate = (stat $file)[9];
+
+    $maxdate = $filedate if ($filedate > $maxdate);
+}
+
+my $date = DateTime->from_epoch(epoch => $maxdate);
+
+printf("%s %s\n",$date->dmy('/'),$date->hms);


Mime
View raw message