summaryrefslogtreecommitdiffstats
path: root/RelationSet.pm
diff options
context:
space:
mode:
Diffstat (limited to 'RelationSet.pm')
-rw-r--r--RelationSet.pm211
1 files changed, 211 insertions, 0 deletions
diff --git a/RelationSet.pm b/RelationSet.pm
new file mode 100644
index 000000000..ee402e7a4
--- /dev/null
+++ b/RelationSet.pm
@@ -0,0 +1,211 @@
+#
+# 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 the Bugzilla Bug Tracking System.
+#
+# The Initial Developer of the Original Code is Netscape Communications
+# Corporation. Portions created by Netscape are
+# Copyright (C) 2000 Netscape Communications Corporation. All
+# Rights Reserved.
+#
+# Contributor(s): Dan Mosedale <dmose@mozilla.org>
+# Terry Weissman <terry@mozilla.org>
+
+# This object models a set of relations between one item and a group
+# of other items. An example is the set of relations between one bug
+# and the users CCed on that bug. Currently, the relation objects are
+# expected to be bugzilla userids. However, this could and perhaps
+# should be generalized to work with non userid objects, such as
+# keywords associated with a bug. That shouldn't be hard to do; it
+# might involve turning this into a virtual base class, and having
+# UserSet and KeywordSet types that inherit from it.
+
+use diagnostics;
+use strict;
+
+require "globals.pl";
+
+package RelationSet;
+use CGI::Carp qw(fatalsToBrowser);
+
+# create a new empty RelationSet
+#
+sub new {
+ my $type = shift();
+
+ # create a ref to an empty hash and bless it
+ #
+ my $self = {};
+ bless $self, $type;
+
+ # construct from a comma-delimited string
+ #
+ if ($#_ == 0) {
+ $self->mergeFromString($_[0]);
+ }
+ # unless this was a constructor for an empty list, somebody screwed up.
+ #
+ elsif ( $#_ != -1 ) {
+ confess("invalid number of arguments");
+ }
+
+ # bless as a RelationSet
+ #
+ return $self;
+}
+
+# Assumes that the set of relations "FROM $table WHERE $constantSql and
+# $column = $value" is currently represented by $self, and this set should
+# be updated to look like $other.
+#
+# Returns an array of two strings, one INSERT and one DELETE, which will
+# make this change. Either or both strings may be the empty string,
+# meaning that no INSERT or DELETE or both (respectively) need to be done.
+#
+# THE CALLER IS RESPONSIBLE FOR ANY DESIRED LOCKING AND/OR CONSISTENCY
+# CHECKS (not to mention doing the SendSQL() calls).
+#
+sub generateSqlDeltas {
+ ($#_ == 5) || confess("invalid number of arguments");
+ my ( $self, # instance ptr to set representing the existing state
+ $endState, # instance ptr to set representing the desired state
+ $table, # table where these relations are kept
+ $invariantName, # column held const for a RelationSet (often "bug_id")
+ $invariantValue, # what to hold the above column constant at
+ $columnName # the column which varies (often a userid)
+ ) = @_;
+
+ # construct the insert list by finding relations which exist in the
+ # end state but not the current state.
+ #
+ my @endStateRelations = keys(%$endState);
+ my @insertList = ();
+ foreach ( @endStateRelations ) {
+ push ( @insertList, $_ ) if ( ! exists $$self{"$_"} );
+ }
+
+ # we've built the list. If it's non-null, add required sql chrome.
+ #
+ my $sqlInsert="";
+ if ( $#insertList > -1 ) {
+ $sqlInsert = "INSERT INTO $table ($invariantName, $columnName) VALUES " .
+ join (",",
+ map ( "($invariantValue, $_)" , @insertList )
+ );
+ }
+
+ # construct the delete list by seeing which relations exist in the
+ # current state but not the end state
+ #
+ my @selfRelations = keys(%$self);
+ my @deleteList = ();
+ foreach ( @selfRelations ) {
+ push (@deleteList, $_) if ( ! exists $$endState{"$_"} );
+ }
+
+ # we've built the list. if it's non-empty, add required sql chrome.
+ #
+ my $sqlDelete = "";
+ if ( $#deleteList > -1 ) {
+ $sqlDelete = "DELETE FROM $table WHERE $invariantName = $invariantValue " .
+ "AND $columnName IN ( " . join (",", @deleteList) . " )";
+ }
+
+ return ($sqlInsert, $sqlDelete);
+}
+
+# compare the current object with another.
+#
+sub isEqual {
+ ($#_ == 1) || confess("invalid number of arguments");
+ my $self = shift();
+ my $other = shift();
+
+ # get arrays of the keys for faster processing
+ #
+ my @selfRelations = keys(%$self);
+ my @otherRelations = keys(%$other);
+
+ # make sure the arrays are the same size
+ #
+ return 0 if ( $#selfRelations != $#otherRelations );
+
+ # bail out if any of the elements are different
+ #
+ foreach my $relation ( @selfRelations ) {
+ return 0 if ( !exists $$other{$relation})
+ }
+
+ # we made it!
+ #
+ return 1;
+
+}
+
+# merge the results of a SQL command into this set
+#
+sub mergeFromDB {
+ ( $#_ == 1 ) || confess("invalid number of arguments");
+ my $self = shift();
+
+ &::SendSQL(shift());
+ while (my @row = &::FetchSQLData()) {
+ $$self{$row[0]} = 1;
+ }
+
+ return;
+}
+
+# merge a set in string form into this set
+#
+sub mergeFromString {
+ ($#_ == 1) || confess("invalid number of arguments");
+ my $self = shift();
+
+ # do the merge
+ #
+ foreach my $person (split(/[ ,]/, shift())) {
+ if ($person ne "") {
+ $$self{&::DBNameToIdAndCheck($person)} = 1;
+ }
+ }
+}
+
+# return the number of elements in this set
+#
+sub size {
+ my $self = shift();
+
+ my @k = keys(%$self);
+ return $#k++;
+}
+
+# return this set in array form
+#
+sub toArray {
+ my $self= shift();
+
+ return keys(%$self);
+}
+
+# return this set in string form (comma-separated and sorted)
+#
+sub toString {
+ ($#_ == 0) || confess("invalid number of arguments");
+ my $self = shift();
+
+ my @result = ();
+ foreach my $i ( keys %$self ) {
+ push @result, &::DBID_to_name($i);
+ }
+
+ return join(',', sort(@result));
+}