PXP::Plugin - Plugin class definition (used only in the


PXP documentation Contained in the PXP distribution.

Index


Code Index:

NAME

Top

  PXP::Plugin - Plugin class definition (used only in the
  internal registry)

SYNOPSIS

Top

<?xml version="1.0"?>

<plugin id="IMC::WebApp::TestPlugin" name="Test plugin" version="0.1" provider-name='IDEALX'>

</plugin>

DESCRIPTION

Top

A plugin groups together a set of extensions and/or extension-points.

A PXP::Plugin represents such a container as it is read and its content loaded into the system. A common interface is provided to access the configuration descriptors and the actual implementation that has been loaded into the system.

NOTE: a PXP::Plugin object is NOT a plugin.

A real plugin, is just a set of Perl modules loaded according to the 'plugin.xml' descriptor file.

Plugin dependencies are supported with the <require> tag.

Limitations

We support only a subset of the Eclipse plugin model.

instantiateExtension($class, @args)

Load a perl class ($class) and instantiate a new object of this class, with optional arguments for the object initializer (@args)

Return the new instance or undef if the class could not be loaded

name, id, version, providerName

Basic accessors for plugin properties.

resourceDir

Accessor for the 'resourceDir' attribute. 'resourceDir' points to the directory containing the resources bundled with the plugin, such as config files, templates, etc.

This method is called by the PluginRegistry as it loads the plugin.

SEE ALSO

Top

PXP::PluginRegistry, PXP::ExtensionPoint, PXP::Extension

See the article on eclipse.org describing the plugin architecture : http://www.eclipse.org/articles/Article-Plug-in-architecture/plugin_architecture.html


PXP documentation Contained in the PXP distribution.
package PXP::Plugin;

use strict;
use warnings;

use PXP::ExtensionPoint;
use PXP::Extension;

use XML::XPath;
use File::Spec;
use Cwd;

use Log::Log4perl qw(get_logger);
my $logger = get_logger(__PACKAGE__);

sub new {
  my $class = shift;
  my $self = {
	      name => '',
	      id => '',
	      version => 0,
	      provider_name => '',
	      extensions => {},
	      extension_points => {},
	      dependencies => []
	     };
  bless $self, $class;
  return $self->init(@_);
}

sub init {
  my $self = shift;
  my %opts = @_;

  # TODO : warn if no directory
  $self->directory($opts{'directory'});

  my $filename = $opts{'filename'} || '';
  if ($filename) {
    my $config = $self->loadLocalizedResource($filename)
      || die "cannot find config for plugin";

    my $xp = XML::XPath->new(xml => $config);

    $self->loadFromXML($xp);
  }

  return $self;
}

sub loadFromXML {
  my $self = shift;
  my $xp = shift;

  # FIXME: Check all values

  # Set plugin params;
  my @nodes = $xp->findnodes('/plugin');
  unless (@nodes) {
    $logger->error("No plugin defined in xml config file. Loading aborted.");
    return undef;
  }

  my $node = shift @nodes;
  # Define parameters
  $self->id($node->getAttribute('id'));
  $self->class($node->getAttribute('class'));
  $self->name($node->getAttribute('name'));
  $self->version($node->getAttribute('version'));
  $self->providerName($node->getAttribute('provider-name'));

  # Handle dependencies
  my @deps = $xp->findnodes('/plugin/require');
  $self->addDependencies(map ($_->getAttribute('plugin'), @deps));

  # Handle libraries
  my @libs = $xp->findnodes('/plugin/runtime/library');
  $self->addLibraries(map ($_->getAttribute('name'), @libs));

  # Save XML node
  $self->{'xml'} = $xp;

  # Add the plugin directory to Perl search path
  unshift @INC, $self->directory();

  # Extensions and ExtensionPoints are no longer instantiated here.
  # This must be done by _instantiateExtensions(), after dependencies
  # have been loaded.
}

sub instantiateExtensions {
  my $self = shift;

  my $xp = $self->{'xml'};
  my $node;
  # Create extension points
  foreach $node ($xp->findnodes('/plugin/extension-point')) {
    my $id = $node->getAttribute('id') || die "bummer";
    my $class = $node->getAttribute('class') || $id; # FIXME: if nothing was specified, do not force a class load
    my $name = $node->getAttribute('name');
    my $version = $node->getAttribute('version');
    my $obj;

    # Instantiate it
    # FIXME: Add test on values
    my $extpt = PXP::ExtensionPoint->new($self);
    if (_loadClass($class)) {
      $obj = $class->new($self);
      # support for Aurelien's approach (an extension point 'IS-A' PXP::ExtensionPoint)
      if ($obj->isa('PXP::ExtensionPoint')) {
        $logger->warn("$id should not be an PXP::ExtensionPoint !");
	undef $extpt;
	$extpt = $obj;
      }
      $extpt->object($obj);
    }
    $extpt->id($id);
    $extpt->class($class);
    $extpt->name($name);
    $extpt->version($version);
    $self->_storeExtensionPoint($extpt);
  }

  # Add extensions
  foreach $node ($xp->findnodes('/plugin/extension')) {

    # Get informations
    my $id = $node->getAttribute('id') || die "bummer";
    my $point = $node->getAttribute('point') || die "bummer";
    my $class = $node->getAttribute('class') || $id;
    my $name = $node->getAttribute('name');
    my $version = $node->getAttribute('version');
    my $obj;

    my $ext = PXP::Extension->new($self);
    $ext->id($id);
    $ext->class($class);
    $ext->name($name);
    $ext->version($version);
    $ext->point($point);
    $ext->node($node);
    $self->_storeExtension($ext);
  }
}

sub instantiateExtension {
  my $self = shift;
  my $class = shift;
  my @args = shift;
  
  # Instantiate
  if (PXP::Plugin::_loadClass($class)) {
    return $class->new(@args);
  } else {
    die "Could not instanciate class $class";
    return undef;
  }
}

sub name {
  my $self = shift;
  if (@_) {
    $self->{name} = shift;
    return $self;
  } else {
    return $self->{name};
  }
}

sub id {
  my $self = shift;
  if (@_) {
    $self->{id} = shift;
    return $self;
  } else {
    return $self->{id};
  }
}

sub class {
  my $self = shift;
  if (@_) {
    $self->{class} = shift;
    return $self;
  } else {
    return $self->{class};
  }
}

sub version {
  my $self = shift;
  if (@_) {
    $self->{version} = shift;
    return $self;
  } else {
    return $self->{version};
  }
}

sub providerName {
  my $self = shift;
  if (@_) {
    $self->{provider_name} = shift;
    return $self;
  } else {
    return $self->{provider_name};
  }
}

sub resourceDir {
  my $self = shift;
  return $self->directory(@_);
}

sub xml {
  my $self = shift;
  return $self->{xml};
}

sub directory {
  my $self = shift;
  if (@_) {
    $self->{directory} = shift;
    return $self;
  } else {
    return $self->{directory};
  }
}

sub fullDirectory {
  my $self = shift;
  return File::Spec->catdir(getcwd(), $self->{directory});
}

sub resource {
  my $self = shift;
  return File::Spec->catfile($self->directory(), @_);
}

sub fullResource {
  my $self = shift;
  return File::Spec->catfile($self->fullDirectory(), @_);
}

sub getDependencies {
  my $self = shift;
  return $self->{'dependencies'};
}

sub addDependencies {
  my $self = shift;
  push @{$self->{'dependencies'}}, @_;
  return $self;
}

sub libraries {
  my $self = shift;
  return $self->{'libraries'};
}

sub addLibraries {
  my $self = shift;
  push @{$self->{'libraries'}}, @_;
  return $self;
}

sub _loadClass {
  my $class = shift;

  eval "require $class";
  if ($@) {
    warn "Could not load $class: " .$@;
    return undef;
  }
  return 1;
}

sub _storeExtensionPoint {
  my $self = shift;
  my $ext = shift;
  $self->{'extension_points'}->{$ext->id()} = $ext;
  return $ext;
}

sub getExtensionPoint {
  my $self = shift;
  my $id = shift;
  return $self->{'extension_points'}->{$id};
}

sub getAllExtensionPoints {
  my $self =  shift;

  if ($self->{'extension_points'}) {
      return (values %{$self->{'extension_points'}});
  }
}

sub _storeExtension {
  my $self = shift;
  my $ext = shift;
  $self->{'extensions'}->{$ext->id()} = $ext;
  # remember extension declaration order in the plugin.xml file
  push @{$self->{'extensions_array'}}, $ext;
  return $ext;
}

sub getExtension {
  my($self) = shift;
  my($id) = shift;
  return($self->{'extensions'}->{$id});
}

# Return an _ordered_ list of extensions for the plugin, so we can control extension registration order
# (this is important for constructing the MainPipeline, and make sure the RequestManager gets called first)

sub getAllExtensions {
  my $self =  shift;

  if ($self->{'extensions_array'}) {
      return @{$self->{'extensions_array'}};
  }
}

use FileHandle;

sub getResourceHandle {
  my $self = shift;
  my $resourceId = shift;

  if (! $self->isa('PXP::Plugin')) {
    $self = PXP::PluginRegistry::getPlugin($self)
      || die "cannot find which plugin I am !";
  }

  my $filename = File::Spec->catfile($self->resourceDir(), $resourceId);
  $logger->debug("resource for " . $self->id() . " should be in " . $filename);

  return new FileHandle $filename;
}

# almost the same code as above, except we try to find a localized property file
# property files are named with the locale and after the original filename,
# but with a .properties extension (instead of the filename original extension, like .xml)
sub getPropertyFileHandle {
  my $self = shift;
  my $resourceId = shift;

  my $dir;
  if (! $self->isa('PXP::Plugin')) {
    $self = PXP::PluginRegistry::getPlugin($self)
      || die "cannot find which plugin I am !";
  }

  $dir = $self->resourceDir();
  use File::Basename;
  my ($filenamePrefix, $dummy, $dummy2) = fileparse($resourceId, qr/\..*/);
  my $filename = File::Spec->catfile($dir, $filenamePrefix);

  $logger->debug('looking for property file ' . $filenamePrefix . '.properties');
  my $pfname = PXP::I18N::getPropertyFile($filename);

  return new FileHandle $pfname;
}

sub loadResource {
  my $self = shift;
  my $resourceId = shift;

  $logger->debug("loading resource $resourceId");
  my $handle = $self->getResourceHandle($resourceId);
  local $/;
  return <$handle>;
}

# same as above, but we look for a property file
# and substitute with the '%property' pattern (same as in Eclipse)
sub loadLocalizedResource {
  my $self = shift;
  my $resourceId = shift;

  $logger->debug("loading localized resource $resourceId");
  my $pfh = $self->getPropertyFileHandle($resourceId);
  unless ($pfh) {
    my $rfh = $self->getResourceHandle($resourceId);
    local $/;
    return <$rfh>;
  }

  my $rfh = $self->getResourceHandle($resourceId);

  return PXP::I18N::loadNLocalize($rfh, $pfh);
}

1;