=head1 NAME

SuikaWiki::Plugin - SuikaWiki: WikiPlugin common interface

=head1 DESCRIPTION

This module provides WikiPlugin interface implementation.

There are two interface: class methods collection and WikiPlugin object.
Class methods collection provides some useful facilities to be
called by WikiPlugin module and/or by contexts using WikiPlugin.

This module is part of SuikaWiki.

=cut

package SuikaWiki::Plugin;
use strict;
our $VERSION = do{my @r=(q$Revision: 1.12 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};

=head1 CLASS METHODS

=over 4

=begin temp

=item $o->new_index ($index_name)

Increments the index number and returns the new index number.

=end temp

=cut

{my %Index;
sub new_index ($$) { ++$Index{$_[1]} }
}

=item $formatter = SuikaWiki::Plugin->formatter ($category_name)

Returns an instance of formatter whose category (context) is
$category_name.

=cut

{my %Formatter;
sub formatter ($$) {
  my $t = $_[1]; $t =~ tr/-/_/;
  unless ($Formatter{$t}) {
    $Formatter{$t} = new SuikaWiki::Plugin::xml_formatter
                       -category_name => $t;
  }
  return $Formatter{$t};
}}

{my %Formatter;
sub text_formatter ($$) {
  my $t = $_[1]; $t =~ tr/-/_/;
  unless ($Formatter{$t}) {
    $Formatter{$t} = new SuikaWiki::Plugin::text_formatter
                       -category_name => $t;
  }
  return $Formatter{$t};
}}

=item $package = SuikaWiki::Plugin->module_package ($plugin_name)

Returns package name of the plugin module named $plugin_name.

=cut

sub module_package ($$) {
  my ($self, $module) = @_;
  my $pack = $SuikaWiki::Plugin::Registry::Info{$module}->{module_name};
  if ($pack) {
    return $pack;
  } else {
    throw SuikaWiki::Plugin::error
      -type => 'PLUGIN_NOT_FOUND',
      module => $module,
      -object => $self, method => 'module_package';
  }
}

=item SuikaWiki::Plugin->load_directory ($dir1, $dir2,...)

Loads plugin modules in specified directories.

=cut

sub load_directory ($;@) {
  my $self = shift;
  for my $dir (@_) {
    opendir PDIR, $dir or throw SuikaWiki::Plugin::error
                            -type => 'PLUGIN_DIRECTORY_CANT_OPEN',
                            file => $dir,
                            message => $!,
                            -object => $self, method => 'load_directory';
    for (grep {substr ($_, -3) eq '.pm'} readdir (PDIR)) {
      eval { require $dir.'/'.$_ };
      if ($@) {
        throw SuikaWiki::Plugin::error
          -type => 'PLUGIN_COMPILE_ERROR',
          file => $dir.'/'.$_,
          message => $@,
          method => 'load_directory', -object => $self;
      }
    }
    closedir PDIR;
  }
}

=back

=head1 WIKIPLUGIN MANAGER OBJECT

This part implements WikiPlugin manager object of SuikaWiki WikiPlugin 
implementation model, second edition.

Note that class methods such as C<< ->load_directory >>
and C<< ->formatter >> as also available as methods of WikiPlugin object,
although result of those operation is still "global".

WikiPlugin manager object is usually instantiated by C<init_plugins>
method of Wiki implementation object (C<SuikaWiki::Implementation> module).

  $wiki = <SuikaWiki::Implementation instance>;
  $wiki->init_plugins;
  $plugin_manager = $wiki->{plugin};

=over 4

=item $p = SuikaWiki::Plugin->new (wiki => $WIKI)

Constructs new instance of WikiPlugin manager

=cut

sub new ($;%) {
  my $class = shift;
  bless {@_}, $class;
}

sub exit ($) {
  my $self = shift;
  delete $self->{wiki};
  $self->{exited} = 1;
}

sub DESTROY ($) {
  my $self = shift;
  $self->exit unless $self->{exited};
}

=item $p->{wiki}

Wiki implementation $p is associated with

=back

=cut

package SuikaWiki::Plugin::xml_formatter;
require Message::Util::Formatter::Node;
our @ISA = 'Message::Util::Formatter::Node';
require Message::Markup::XML::Node;

sub ___rule_def ($) {
  $SuikaWiki::Plugin::Rule{$_[0]->{-category_name}} || {};
}

sub replace_option () {+{
    -class => 'Message::Markup::XML::Node',
}}

package SuikaWiki::Plugin::text_formatter;
require Message::Util::Formatter::Text;
our @ISA = 'Message::Util::Formatter::Text';

sub ___rule_def ($) {
  $SuikaWiki::Plugin::Rule{$_[0]->{-category_name}} || {};
}

package SuikaWiki::Plugin::error;
require Message::Util::Error;
our @ISA = 'Message::Util::Error';

=head1 EXCEPTIONS

=over 4

=item PLUGIN_COMPILE_ERROR

Something wrong while loading WikiPlugin module file C<file>,
because of C<message>.

=item PLUGIN_DIRECTORY_CANT_OPEN

WikiPlugin directory C<file> cannot be opened, because of C<message>.

=item PLUGIN_NOT_FOUND

WikiPlugin module C<module> not found.

=cut

sub ___error_def () {+{
  PLUGIN_COMPILE_ERROR => {
    description => q(%t(name => method);: %t(name => file);: %t(name => message);),
  },
  PLUGIN_DIRECTORY_CANT_OPEN => {
    description => q(%t(name => method);: %t(name => file);: %t(name => message);),
  },
  PLUGIN_NOT_FOUND => {
    description => q(%t(name => method);: WikiPlugin module "%t(name => module);" not loaded),
  },
}}

=back

=head1 SEE ALSO

<IW:SuikaWiki:SuikaWiki:WikiPlugin>

C<lib/SuikaWiki/Plugin/*>,
C<misc/pluging/*>

=head1 LICENSE

Copyright 2002-2003 Wakaba <w@suika.fam.cx>

This program is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.

=cut

1; # $Date: 2004/01/16 08:04:59 $