gecko-dev/webtools/PLIF/PLIF.pm

311 строки
9.0 KiB
Perl
Исходник Обычный вид История

2001-05-05 11:12:56 +04:00
# -*- Mode: perl; tab-width: 4; indent-tabs-mode: nil; -*-
#
# This file is MPL/GPL dual-licensed under the following terms:
#
# The contents of this file are subject to the Mozilla Public License
# Version 1.1 (the "License"); you may not use this file except in
# compliance with the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS"
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
# the License for the specific language governing rights and
# limitations under the License.
#
# The Original Code is PLIF 1.0.
# The Initial Developer of the Original Code is Ian Hickson.
#
# Alternatively, the contents of this file may be used under the terms
# of the GNU General Public License Version 2 or later (the "GPL"), in
# which case the provisions of the GPL are applicable instead of those
# above. If you wish to allow use of your version of this file only
# under the terms of the GPL and not to allow others to use your
# version of this file under the MPL, indicate your decision by
# deleting the provisions above and replace them with the notice and
# other provisions required by the GPL. If you do not delete the
# provisions above, a recipient may use your version of this file
# under either the MPL or the GPL.
package PLIF;
use strict; # require strict adherence to perl standards
use vars qw($AUTOLOAD); # it's a package global
use Carp qw(cluck confess); # stack trace versions of warn and die
my $DEBUG = 4; # level of warnings and dumps to print to STDERR (none go to user)
my $USER = 1; # level of errors to report to user (all go to STDERR)
2001-05-05 11:12:56 +04:00
my @FATAL = (); # a list of pointers to functions that want to report errors to the user
my $LOCKED = 0; # set to '1' while we are calling the error reporting code
1;
# PLIF = Program Logic Insulation Framework
# Levels are assumed to be something along the following:
# Things that should never come up during normal operation:
2001-05-05 11:12:56 +04:00
# 0 = total failure: e.g. no input or output devices
# 1 = fatal errors: e.g. missing databases, broken connections, out of disk space
# 2 = security: e.g. warnings about repeated cracking attempts
# 3 = non-fatal errors: e.g. propagation of eval() errors as warnings
# 4 = important warnings (e.g. unexpected but possibly legitimate lack of data)
#
# Useful debugging information:
2001-05-05 11:12:56 +04:00
# 5 = important events (e.g. application started)
# 6 = debugging remarks for the section currently under test
2001-05-05 11:12:56 +04:00
# 7 = typical checkpoints (e.g. someone tried to do some output)
# 8 = frequently hit typical checkpoints
2001-05-05 11:12:56 +04:00
# 9 = verbose debugging information
# 10 = ridiculously verbose debugging spam
#
# No code in CVS should do anything at level 6, it is reserved for
# personal debugging.
2001-05-05 11:12:56 +04:00
# Note. All of the methods described in this class except for the
# propertyGet, propertySet and propertyExists methods are class
# methods. You can call "$class->notImplemented" without a problem.
# provide a standard virtual constructor
# if already created, merely return $self
sub create {
my $class = shift;
if (ref($class)) {
return $class; # already created, return self
} else {
my $self = $class->bless(@_); # call our real constructor
* Created service- and serviceInstance- specific constructors so that a single module can have distinct constructors depending on how it is invoked. These constructors are called serviceInit() and serviceInstanceInit(), and both chain to the existing init() constructor (so no change are required by this). * Changed FileStrings so that it doesn't have to be updated every time the file string format changes. The format is now one line per piece of metadata, then a blank line, then the string data. If additional metadata is added later then this will automatically support it. * Renamed the dataSource.strings service to dataSource.strings.customised, and renamed its get() method to getCustomisedString(). * Changed the semantics of dataSource.strings.customised so that it no longer looks for a default string if it can't find a customised one (and thus removed getDefaultString). * Abstracted the Generic output module even more. It now consists of output.generic (a service instance with its own constructor) and dataSource.strings (a pure service), the latter of which is a wrapper around dataSource.strings.customised and dataSource.strings.defaults. * Updated Coses to work with the new dataSource.strings insterface. * Removed the test app DataSource::ConsoleStrings and DataSource::HTTPStrings files, since they were redundant with the default output files. * Removed all the default strings in the CosesEditor and Login components since they are pretty pointless. * Factored out the call to dump() in the GenericOutputs module. * Changed setString in the MySQL string data source so that it will now add a blank string (it used to delete the string if it was blank, but that meant that it was not possible to customise strings away). * Added a piece of metadata to strings: their version number. * Updated the customised string data source stubs to mention the version data now stored with all strings * Added a getAllStringVersions method to the customised strings data source which returns all the string names and their version numbers. * Made the customised strings data source check the version number of every string in its database during setupInstall to make sure that they are all up to date, version-wise. If any are out of date, the user is notified. * Added support for the new version column to the MySQL version of the customised string data source. * Updated the increasingly misnamed CosesEditor to support the versioned strings stuff. * Factored out some code in the CosesEditor. * Added version information to all default strings. All default strings are now at version 1. * Fixed a typo in a FileStrings dump statement. * Fixed the calls to setProgress in the MySQL user and strings data sources and in the user field factory to use the correct syntax (a parsable dotted string instead of unlocalisable plain English). * Updated the Generic output module's documentation to match what now happens.
2001-11-22 18:46:42 +03:00
$self->serviceInit(@_);
return $self;
2001-05-05 11:12:56 +04:00
}
}
# provide a constructor that always constructs a new copy of the
# class. This is used to create service instances.
sub serviceCreate {
my $class = shift;
if (ref($class)) {
$class = ref($class);
}
my $self = $class->bless(@_); # call our real constructor
* Created service- and serviceInstance- specific constructors so that a single module can have distinct constructors depending on how it is invoked. These constructors are called serviceInit() and serviceInstanceInit(), and both chain to the existing init() constructor (so no change are required by this). * Changed FileStrings so that it doesn't have to be updated every time the file string format changes. The format is now one line per piece of metadata, then a blank line, then the string data. If additional metadata is added later then this will automatically support it. * Renamed the dataSource.strings service to dataSource.strings.customised, and renamed its get() method to getCustomisedString(). * Changed the semantics of dataSource.strings.customised so that it no longer looks for a default string if it can't find a customised one (and thus removed getDefaultString). * Abstracted the Generic output module even more. It now consists of output.generic (a service instance with its own constructor) and dataSource.strings (a pure service), the latter of which is a wrapper around dataSource.strings.customised and dataSource.strings.defaults. * Updated Coses to work with the new dataSource.strings insterface. * Removed the test app DataSource::ConsoleStrings and DataSource::HTTPStrings files, since they were redundant with the default output files. * Removed all the default strings in the CosesEditor and Login components since they are pretty pointless. * Factored out the call to dump() in the GenericOutputs module. * Changed setString in the MySQL string data source so that it will now add a blank string (it used to delete the string if it was blank, but that meant that it was not possible to customise strings away). * Added a piece of metadata to strings: their version number. * Updated the customised string data source stubs to mention the version data now stored with all strings * Added a getAllStringVersions method to the customised strings data source which returns all the string names and their version numbers. * Made the customised strings data source check the version number of every string in its database during setupInstall to make sure that they are all up to date, version-wise. If any are out of date, the user is notified. * Added support for the new version column to the MySQL version of the customised string data source. * Updated the increasingly misnamed CosesEditor to support the versioned strings stuff. * Factored out some code in the CosesEditor. * Added version information to all default strings. All default strings are now at version 1. * Fixed a typo in a FileStrings dump statement. * Fixed the calls to setProgress in the MySQL user and strings data sources and in the user field factory to use the correct syntax (a parsable dotted string instead of unlocalisable plain English). * Updated the Generic output module's documentation to match what now happens.
2001-11-22 18:46:42 +03:00
$self->serviceInstanceInit(@_);
return $self;
}
sub init {} # stub for services
* Created service- and serviceInstance- specific constructors so that a single module can have distinct constructors depending on how it is invoked. These constructors are called serviceInit() and serviceInstanceInit(), and both chain to the existing init() constructor (so no change are required by this). * Changed FileStrings so that it doesn't have to be updated every time the file string format changes. The format is now one line per piece of metadata, then a blank line, then the string data. If additional metadata is added later then this will automatically support it. * Renamed the dataSource.strings service to dataSource.strings.customised, and renamed its get() method to getCustomisedString(). * Changed the semantics of dataSource.strings.customised so that it no longer looks for a default string if it can't find a customised one (and thus removed getDefaultString). * Abstracted the Generic output module even more. It now consists of output.generic (a service instance with its own constructor) and dataSource.strings (a pure service), the latter of which is a wrapper around dataSource.strings.customised and dataSource.strings.defaults. * Updated Coses to work with the new dataSource.strings insterface. * Removed the test app DataSource::ConsoleStrings and DataSource::HTTPStrings files, since they were redundant with the default output files. * Removed all the default strings in the CosesEditor and Login components since they are pretty pointless. * Factored out the call to dump() in the GenericOutputs module. * Changed setString in the MySQL string data source so that it will now add a blank string (it used to delete the string if it was blank, but that meant that it was not possible to customise strings away). * Added a piece of metadata to strings: their version number. * Updated the customised string data source stubs to mention the version data now stored with all strings * Added a getAllStringVersions method to the customised strings data source which returns all the string names and their version numbers. * Made the customised strings data source check the version number of every string in its database during setupInstall to make sure that they are all up to date, version-wise. If any are out of date, the user is notified. * Added support for the new version column to the MySQL version of the customised string data source. * Updated the increasingly misnamed CosesEditor to support the versioned strings stuff. * Factored out some code in the CosesEditor. * Added version information to all default strings. All default strings are now at version 1. * Fixed a typo in a FileStrings dump statement. * Fixed the calls to setProgress in the MySQL user and strings data sources and in the user field factory to use the correct syntax (a parsable dotted string instead of unlocalisable plain English). * Updated the Generic output module's documentation to match what now happens.
2001-11-22 18:46:42 +03:00
sub serviceInit {
my $self = shift;
$self->init(@_);
}
sub serviceInstanceInit {
my $self = shift;
$self->init(@_);
}
2001-05-05 11:12:56 +04:00
# provide a constructor that always constructs a new copy of the
# class. This is used by services that implement factories for objects
# implemented in the same class (e.g., session objects do this).
sub objectCreate {
2001-05-05 11:12:56 +04:00
my $class = shift;
if (ref($class)) {
$class = ref($class);
}
my $self = $class->bless(@_); # call our real constructor
$self->objectInit(@_);
return $self;
}
sub objectInit {} # stub for objects
# internals of create and objectCreate
sub bless {
my $class = shift;
2001-05-05 11:12:56 +04:00
my $self = {};
CORE::bless($self, $class);
return $self;
}
# provide method-like access for any scalars in $self
sub AUTOLOAD {
my $self = shift;
my $name = $AUTOLOAD;
$name =~ s/^.*://o; # strip fully-qualified portion
if ($self->propertyImpliedAccessAllowed($name)) {
if (scalar(@_) == 1) {
return $self->propertySet($name, @_);
} elsif (scalar(@_) == 0) {
if ($self->propertyExists($name)) {
return $self->propertyGet($name);
} else {
return $self->propertyGetUndefined($name);
}
}
}
$self->methodMissing($name, @_);
2001-05-05 11:12:56 +04:00
}
sub propertySet {
# this is not a class method
my $self = shift;
my($name, $value) = @_;
return $self->{$name} = $value;
}
sub propertyExists {
# this is not a class method
my $self = shift;
my($name) = @_;
$self->assert($name, 0, 'propertyExists() cannot be called without arguments');
return exists($self->{$name});
}
sub propertyImpliedAccessAllowed {
# this is not (supposed to be) a class method
# my $self = shift;
# my($name) = @_;
# $self->assert($name, 0, 'propertyImpliedAccessAllowed() cannot be called without arguments');
return 1;
}
sub propertyGet {
# this is not a class method
my $self = shift;
my($name) = @_;
return $self->{$name};
}
sub propertyGetUndefined {
return undef;
}
sub methodMissing {
my $self = shift;
my($method) = @_;
$self->error(0, "Internal Error: Tried to access non-existent method '$method' in object '$self'");
2001-05-05 11:12:56 +04:00
}
# DEBUGGING AIDS
sub dump {
my $self = shift;
my($level, @data) = @_;
if ($self->isAtDebugLevel($level)) {
foreach (@data) {
print STDERR "$0: ($level) $_\n";
2001-05-05 11:12:56 +04:00
}
}
}
sub warn {
my $self = shift;
my($level, @data) = @_;
if ($self->isAtDebugLevel($level)) {
$self->dump($level, ('-'x12).' Start of Warning Stack Trace '.('-'x12));
cluck(@data); # warn with stack trace
$self->dump($level, ('-'x12). ('-'x30) .('-'x12));
}
}
sub error {
my $self = shift;
my($level, @data) = @_;
$self->dump(9, "error raised: $data[0]");
if ($self->isAtUserLevel($level) and not $LOCKED) {
# XXX this gets called even in eval{} blocks
2001-05-05 11:12:56 +04:00
$LOCKED = 1;
$self->dump(10, 'calling @FATAL error handlers...');
foreach my $entry (@FATAL) {
eval {
&{$entry->[1]}(@data);
};
if ($@) {
$self->warn(3, 'Error occured during \@FATAL callback of object \''.($entry->[0])."': $@");
}
}
$self->dump(10, 'done calling @FATAL error handlers');
$LOCKED = 0;
}
confess(@data); # die with stack trace
}
# this should not be called with the @data containing a trailing dot
2001-05-05 11:12:56 +04:00
sub assert {
my $self = shift;
my($condition, $level, @data) = @_;
if (not $condition) {
$self->error($level, @data);
}
}
sub debug {
my $self = shift;
$self->dump(6, @_);
}
2001-05-05 11:12:56 +04:00
sub notImplemented {
my $self = shift;
$self->error(0, 'Internal Error: Method not implemented');
}
# returns true only if the argument is a debug level that is at least
# as important as the local value of $DEBUG.
sub isAtDebugLevel {
my $self = shift;
my($level) = @_;
return ($level <= $DEBUG);
}
# returns true only if the argument is a debug level that is at least
# as important as the local value of $USER.
sub isAtUserLevel {
my $self = shift;
my($level) = @_;
return ($level <= $USER);
}
# returns a reference to the $DEBUG variable for configuration
# purposes
sub getDebugLevel {
return \$DEBUG;
}
# returns a reference to the $USER variable for configuration purposes
sub getUserLevel {
return \$USER;
}
# returns a reference to the @FATAL variable for modules that have
# very exotic needs
sub getFatalHandlerList {
return \@FATAL;
}
# returns a reference to the $LOCKED variable for modules that which
# to block @FATAL reporting
sub getFatalHandlerLock {
return \$LOCKED;
}
# if you call this, make sure that you call the next function too,
# guarenteed, otherwise you will never be freed until the app dies.
# of course, if you _are_ the app then I guess it's ok...
sub enableErrorReporting {
my $self = shift;
push(@FATAL, [$self, sub { $self->fatalError(@_); }]);
}
sub disableErrorReporting {
my $self = shift;
my @OLDFATAL = @FATAL;
@FATAL = ();
foreach my $entry (@OLDFATAL) {
if ($entry->[0] != $self) {
push(@FATAL, $entry);
}
}
}
sub fatalError {} # stub
sub DESTROY {
my $self = shift;
$self->dump(10, "Called destructor of object $self...");
}