[system] / trunk / webwork2 / lib / WeBWorK / URLPath.pm Repository:
ViewVC logotype

View of /trunk/webwork2/lib/WeBWorK/URLPath.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2162 - (download) (as text) (annotate)
Sat May 22 21:25:14 2004 UTC (9 years ago) by apizer
File size: 26104 byte(s)
Added paths to Student Progress

    1 ################################################################################
    2 # WeBWorK Online Homework Delivery System
    3 # Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/
    4 # $CVSHeader: webwork-modperl/lib/WeBWorK/URLPath.pm,v 1.12 2004/05/03 20:56:06 jj Exp $
    5 #
    6 # This program is free software; you can redistribute it and/or modify it under
    7 # the terms of either: (a) the GNU General Public License as published by the
    8 # Free Software Foundation; either version 2, or (at your option) any later
    9 # version, or (b) the "Artistic License" which comes with this package.
   10 #
   11 # This program is distributed in the hope that it will be useful, but WITHOUT
   12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
   13 # FOR A PARTICULAR PURPOSE.  See either the GNU General Public License or the
   14 # Artistic License for more details.
   15 ################################################################################
   16 
   17 package WeBWorK::URLPath;
   18 
   19 =head1 NAME
   20 
   21 WeBWorK::URLPath - the WeBWorK virtual URL heirarchy.
   22 
   23 =cut
   24 
   25 use strict;
   26 use warnings;
   27 
   28 sub debug {
   29 # my ($label, $indent, @message) = @_;
   30 # print STDERR " "x$indent;
   31 # print STDERR "$label: " if $label ne "";
   32 # print STDERR @message;
   33 }
   34 
   35 =head1 VIRTUAL HEIRARCHY
   36 
   37 PLEASE FOR THE LOVE OF GOD UPDATE THIS IF YOU CHANGE THE HEIRARCHY BELOW!!!
   38 
   39  root                                /
   40 
   41  course_admin                        /admin/ -> logout, options, instructor_tools
   42  set_list                            /$courseID/
   43 
   44  equation_display                    /$courseID/equation/
   45  feedback                            /$courseID/feedback/
   46  gateway_quiz                        /$courseID/quiz_mode/$setID/
   47  grades                              /$courseID/grades/
   48  hardcopy                            /$courseID/hardcopy/
   49  hardcopy_preselect_set              /$courseID/hardcopy/$setID/
   50  logout                              /$courseID/logout/
   51  options                             /$courseID/options/
   52 
   53  instructor_tools                    /$courseID/instructor/
   54 
   55  instructor_user_list                /$courseID/instructor/users/
   56  instructor_user_detail              /$courseID/instructor/users/$userID/
   57  instructor_sets_assigned_to_user    /$courseID/instructor/users/$userID/sets/
   58 
   59  instructor_set_list                 /$courseID/instructor/sets/
   60  instructor_set_detail               /$courseID/instructor/sets/$setID/
   61  instructor_problem_list             /$courseID/instructor/sets/$setID/problems/
   62  [instructor_problem_detail]         /$courseID/instructor/sets/$setID/problems/$problemID/
   63  instructor_users_assigned_to_set    /$courseID/instructor/sets/$setID/users/
   64 
   65  instructor_add_users                /$courseID/instructor/add_users/
   66  instructor_set_assigner             /$courseID/instructor/assigner/
   67  instructor_file_transfer            /$courseID/instructor/files/
   68  instructor_set_maker                /$courseID/instructor/setmaker/
   69 
   70  instructor_problem_editor           /$courseID/instructor/pgProblemEditor/
   71  instructor_problem_editor_withset   /$courseID/instructor/pgProblemEditor/$setID/
   72  instructor_problem_editor_withset_withproblem
   73                                      /$courseID/instructor/pgProblemEditor/$setID/$problemID/
   74 
   75  instructor_scoring                  /$courseID/instructor/scoring/
   76  instructor_scoring_download         /$courseID/instructor/scoringDownload/
   77  instructor_mail_merge               /$courseID/instructor/send_mail/
   78  instructor_answer_log               /$courseID/instructor/show_answers/
   79 
   80  instructor_statistics               /$courseID/instructor/stats/
   81  instructor_set_statistics           /$courseID/instructor/stats/set/$setID/
   82  instructor_user_statistics          /$courseID/instructor/stats/student/$userID/
   83 
   84  instructor_progress                  /$courseID/instructor/StudentProgress/
   85  instructor_set_progress              /$courseID/instructor/StudentProgress/set/$setID/
   86  instructor_user_progress             /$courseID/instructor/StudentProgress/student/$userID/
   87 
   88  problem_list                        /$courseID/$setID/
   89  problem_detail                      /$courseID/$setID/$problemID/
   90 
   91 =cut
   92 
   93 ################################################################################
   94 # tree of path types
   95 ################################################################################
   96 
   97 our %pathTypes = (
   98   root => {
   99     name    => 'WeBWorK',
  100     parent  => '',
  101     kids    => [ qw/course_admin set_list/ ],
  102     match   => qr|^/|,
  103     capture => [ qw// ],
  104     produce => '/',
  105     display => 'WeBWorK::ContentGenerator::Home',
  106   },
  107   course_admin => {
  108     name    => 'Course Administration',
  109     parent  => 'root',
  110     kids    => [ qw/logout options instructor_tools/ ],
  111     match   => qr|^(admin)/|,
  112     capture => [ qw/courseID/ ],
  113     produce => 'admin/',
  114     display => 'WeBWorK::ContentGenerator::CourseAdmin',
  115   },
  116 
  117   ################################################################################
  118 
  119   set_list => {
  120     name    => '$courseID',
  121     parent  => 'root',
  122     kids    => [ qw/equation_display feedback gateway_quiz grades hardcopy
  123       logout options instructor_tools problem_list
  124     / ],
  125     match   => qr|^([^/]+)/|,
  126     capture => [ qw/courseID/ ],
  127     produce => '$courseID/',
  128     display => 'WeBWorK::ContentGenerator::ProblemSets',
  129   },
  130 
  131   ################################################################################
  132 
  133   equation_display => {
  134     name    => 'Equation Display',
  135     parent  => 'set_list',
  136     kids    => [ qw// ],
  137     match   => qr|^equation/|,
  138     capture => [ qw// ],
  139     produce => 'equation/',
  140     display => 'WeBWorK::ContentGenerator::EquationDisplay',
  141   },
  142   feedback => {
  143     name    => 'Feedback',
  144     parent  => 'set_list',
  145     kids    => [ qw// ],
  146     match   => qr|^feedback/|,
  147     capture => [ qw// ],
  148     produce => 'feedback/',
  149     display => 'WeBWorK::ContentGenerator::Feedback',
  150   },
  151   gateway_quiz => {
  152     name    => 'Gateway Quiz $setID',
  153     parent  => 'set_list',
  154     kids    => [ qw// ],
  155     match   => qr|^quiz_mode/([^/]+)/|,
  156     capture => [ qw/setID/ ],
  157     produce => 'quiz_mode/$setID/',
  158     display => 'WeBWorK::ContentGenerator::GatewayQuiz',
  159   },
  160   grades => {
  161     name    => 'Student Grades',
  162     parent  => 'set_list',
  163     kids    => [ qw// ],
  164     match   => qr|^grades/|,
  165     capture => [ qw// ],
  166     produce => 'grades/',
  167     display => 'WeBWorK::ContentGenerator::Grades',
  168   },
  169   hardcopy => {
  170     name    => 'Hardcopy Generator',
  171     parent  => 'set_list',
  172     kids    => [ qw/hardcopy_preselect_set/ ],
  173     match   => qr|^hardcopy/|,
  174     capture => [ qw// ],
  175     produce => 'hardcopy/',
  176     display => 'WeBWorK::ContentGenerator::Hardcopy',
  177   },
  178   hardcopy_preselect_set => {
  179     name    => 'Hardcopy Generator',
  180     parent  => 'hardcopy',
  181     kids    => [ qw// ],
  182     match   => qr|^([^/]+)/|,
  183     capture => [ qw/setID/ ],
  184     produce => '$setID/',
  185     display => 'WeBWorK::ContentGenerator::Hardcopy',
  186   },
  187   logout => {
  188     name    => 'Log Out',
  189     parent  => 'set_list',
  190     kids    => [ qw// ],
  191     match   => qr|^logout/|,
  192     capture => [ qw// ],
  193     produce => 'logout/',
  194     display => 'WeBWorK::ContentGenerator::Logout',
  195   },
  196   options => {
  197     name    => 'User Options',
  198     parent  => 'set_list',
  199     kids    => [ qw// ],
  200     match   => qr|^options/|,
  201     capture => [ qw// ],
  202     produce => 'options/',
  203     display => 'WeBWorK::ContentGenerator::Options',
  204   },
  205 
  206   ################################################################################
  207 
  208   instructor_tools => {
  209     name    => 'Instructor Tools',
  210     parent  => 'set_list',
  211     kids    => [ qw/instructor_user_list instructor_set_list instructor_add_users
  212       instructor_set_assigner instructor_file_transfer
  213       instructor_problem_editor instructor_set_maker
  214       instructor_scoring instructor_scoring_download instructor_mail_merge
  215       instructor_answer_log instructor_statistics
  216       instructor_progress
  217     / ],
  218     match   => qr|^instructor/|,
  219     capture => [ qw// ],
  220     produce => 'instructor/',
  221     display => 'WeBWorK::ContentGenerator::Instructor::Index',
  222   },
  223 
  224   ################################################################################
  225 
  226   instructor_user_list => {
  227     name    => 'User List',
  228     parent  => 'instructor_tools',
  229     kids    => [ qw/instructor_user_detail/ ],
  230     match   => qr|^users/|,
  231     capture => [ qw// ],
  232     produce => 'users/',
  233     display => 'WeBWorK::ContentGenerator::Instructor::UserList',
  234   },
  235   instructor_user_detail => {
  236     name    => '$userID',
  237     parent  => 'instructor_user_list',
  238     kids    => [ qw/instructor_sets_assigned_to_user/ ],
  239     match   => qr|^([^/]+)/|,
  240     capture => [ qw/userID/ ],
  241     produce => '$userID/',
  242     display => 'WeBWorK::ContentGenerator::Instructor::UserDetail',
  243   },
  244   instructor_sets_assigned_to_user => {
  245     name    => 'Sets Assigned to User',
  246     parent  => 'instructor_user_detail',
  247     kids    => [ qw// ],
  248     match   => qr|^sets/|,
  249     capture => [ qw// ],
  250     produce => 'sets/',
  251     display => 'WeBWorK::ContentGenerator::Instructor::SetsAssignedToUser',
  252   },
  253 
  254   ################################################################################
  255 
  256   instructor_set_list => {
  257     name    => 'Set List',
  258     parent  => 'instructor_tools',
  259     kids    => [ qw/instructor_set_detail/ ],
  260     match   => qr|^sets/|,
  261     capture => [ qw// ],
  262     produce => 'sets/',
  263     display => 'WeBWorK::ContentGenerator::Instructor::ProblemSetList',
  264   },
  265   instructor_set_detail => {
  266     name    => '$setID',
  267     parent  => 'instructor_set_list',
  268     kids    => [ qw/instructor_problem_list instructor_users_assigned_to_set/ ],
  269     match   => qr|^([^/]+)/|,
  270     capture => [ qw/setID/ ],
  271     produce => '$setID/',
  272     display => 'WeBWorK::ContentGenerator::Instructor::ProblemSetEditor',
  273   },
  274   instructor_problem_list => {
  275     name    => 'Problems',
  276     parent  => 'instructor_set_detail',
  277     kids    => [ qw// ],
  278     match   => qr|^problems/|,
  279     capture => [ qw// ],
  280     produce => 'problems/',
  281     display => 'WeBWorK::ContentGenerator::Instructor::ProblemList',
  282   },
  283   instructor_users_assigned_to_set => {
  284     name    => 'Users Assigned to Set',
  285     parent  => 'instructor_set_detail',
  286     kids    => [ qw// ],
  287     match   => qr|^users/|,
  288     capture => [ qw// ],
  289     produce => 'users/',
  290     display => 'WeBWorK::ContentGenerator::Instructor::UsersAssignedToSet',
  291   },
  292 
  293   ################################################################################
  294 
  295   instructor_add_users => {
  296     name    => 'Add Users',
  297     parent  => 'instructor_tools',
  298     kids    => [ qw// ],
  299     match   => qr|^add_users/|,
  300     capture => [ qw// ],
  301     produce => 'add_users/',
  302     display => 'WeBWorK::ContentGenerator::Instructor::AddUsers',
  303   },
  304   instructor_set_assigner => {
  305     name    => 'Set Assigner',
  306     parent  => 'instructor_tools',
  307     kids    => [ qw// ],
  308     match   => qr|^assigner/|,
  309     capture => [ qw// ],
  310     produce => 'assigner/',
  311     display => 'WeBWorK::ContentGenerator::Instructor::Assigner',
  312   },
  313   instructor_set_maker => {
  314     name    => 'Set Maker',
  315     parent  => 'instructor_tools',
  316     kids    => [ qw// ],
  317     match   => qr|^setmaker/|,
  318     capture => [ qw// ],
  319     produce => 'setmaker/',
  320     display => 'WeBWorK::ContentGenerator::Instructor::SetMaker',
  321   },
  322   instructor_file_transfer => {
  323     name    => 'File Transfer',
  324     parent  => 'instructor_tools',
  325     kids    => [ qw// ],
  326     match   => qr|^files/|,
  327     capture => [ qw// ],
  328     produce => 'files/',
  329     display => 'WeBWorK::ContentGenerator::Instructor::FileXfer',
  330   },
  331   instructor_problem_editor => {
  332     name    => 'Problem Editor',
  333     parent  => 'instructor_tools',
  334     kids    => [ qw/instructor_problem_editor_withset/ ],
  335     match   => qr|^pgProblemEditor/|,
  336     capture => [ qw// ],
  337     produce => 'pgProblemEditor/',
  338     display => 'WeBWorK::ContentGenerator::Instructor::PGProblemEditor',
  339   },
  340   instructor_problem_editor_withset => {
  341     name    => '$setID',
  342     parent  => 'instructor_problem_editor',
  343     kids    => [ qw/instructor_problem_editor_withset_withproblem/ ],
  344     match   => qr|^([^/]+)/|,
  345     capture => [ qw/setID/ ],
  346     produce => '$setID/',
  347     display => '',
  348   },
  349   instructor_problem_editor_withset_withproblem => {
  350     name    => '$problemID',
  351     parent  => 'instructor_problem_editor_withset',
  352     kids    => [ qw// ],
  353     match   => qr|^([^/]+)/|,
  354     capture => [ qw/problemID/ ],
  355     produce => '$problemID/',
  356     display => 'WeBWorK::ContentGenerator::Instructor::PGProblemEditor',
  357   },
  358   instructor_scoring => {
  359     name    => 'Scoring Tools',
  360     parent  => 'instructor_tools',
  361     kids    => [ qw// ],
  362     match   => qr|^scoring/|,
  363     capture => [ qw// ],
  364     produce => 'scoring/',
  365     display => 'WeBWorK::ContentGenerator::Instructor::Scoring',
  366   },
  367   instructor_scoring_download => {
  368     name    => 'Scoring Download',
  369     parent  => 'instructor_tools',
  370     kids    => [ qw// ],
  371     match   => qr|^scoringDownload/|,
  372     capture => [ qw// ],
  373     produce => 'scoringDownload/',
  374     display => 'WeBWorK::ContentGenerator::Instructor::ScoringDownload',
  375   },
  376   instructor_mail_merge => {
  377     name    => 'Mail Merge',
  378     parent  => 'instructor_tools',
  379     kids    => [ qw// ],
  380     match   => qr|^send_mail/|,
  381     capture => [ qw// ],
  382     produce => 'send_mail/',
  383     display => 'WeBWorK::ContentGenerator::Instructor::SendMail',
  384   },
  385   instructor_answer_log => {
  386     name    => 'Answer Log',
  387     parent  => 'instructor_tools',
  388     kids    => [ qw// ],
  389     match   => qr|^show_answers/|,
  390     capture => [ qw// ],
  391     produce => 'show_answers/',
  392     display => 'WeBWorK::ContentGenerator::Instructor::ShowAnswers',
  393   },
  394 
  395   ################################################################################
  396 
  397   instructor_statistics => {
  398     name    => 'Statistics',
  399     parent  => 'instructor_tools',
  400     kids    => [ qw/instructor_set_statistics instructor_user_statistics/ ],
  401     match   => qr|^stats/|,
  402     capture => [ qw// ],
  403     produce => 'stats/',
  404     display => 'WeBWorK::ContentGenerator::Instructor::Stats',
  405   },
  406   instructor_set_statistics => {
  407     name    => 'Statistics',
  408     parent  => 'instructor_statistics',
  409     kids    => [ qw// ],
  410     match   => qr|^(set)/([^/]+)/|,
  411     capture => [ qw/statType setID/ ],
  412     produce => 'set/$setID/',
  413     display => 'WeBWorK::ContentGenerator::Instructor::Stats',
  414   },
  415   instructor_user_statistics => {
  416     name    => 'Statistics',
  417     parent  => 'instructor_statistics',
  418     kids    => [ qw// ],
  419     match   => qr|^(student)/([^/]+)/|,
  420     capture => [ qw/statType userID/ ],
  421     produce => 'student/$userID/',
  422     display => 'WeBWorK::ContentGenerator::Instructor::Stats',
  423   },
  424 
  425   ################################################################################
  426 
  427   instructor_progress => {
  428     name    => 'StudentProgress',
  429     parent  => 'instructor_tools',
  430     kids    => [ qw/instructor_set_progress instructor_user_progress/ ],
  431     match   => qr|^progress/|,
  432     capture => [ qw// ],
  433     produce => 'progress/',
  434     display => 'WeBWorK::ContentGenerator::Instructor::StudentProgress',
  435   },
  436   instructor_set_progress => {
  437     name    => 'StudentProgress',
  438     parent  => 'instructor_progress',
  439     kids    => [ qw// ],
  440     match   => qr|^(set)/([^/]+)/|,
  441     capture => [ qw/statType setID/ ],
  442     produce => 'set/$setID/',
  443     display => 'WeBWorK::ContentGenerator::Instructor::StudentProgress',
  444   },
  445   instructor_user_progress => {
  446     name    => 'StudentProgress',
  447     parent  => 'instructor_progress',
  448     kids    => [ qw// ],
  449     match   => qr|^(student)/([^/]+)/|,
  450     capture => [ qw/statType userID/ ],
  451     produce => 'student/$userID/',
  452     display => 'WeBWorK::ContentGenerator::Instructor::StudentProgress',
  453   },
  454 
  455   ################################################################################
  456 
  457   problem_list => {
  458     name    => '$setID',
  459     parent  => 'set_list',
  460     kids    => [ qw/problem_detail/ ],
  461     match   => qr|^([^/]+)/|,
  462     capture => [ qw/setID/ ],
  463     produce => '$setID/',
  464     display => 'WeBWorK::ContentGenerator::ProblemSet',
  465   },
  466   problem_detail => {
  467     name    => '$problemID',
  468     parent  => 'problem_list',
  469     kids    => [ qw// ],
  470     match   => qr|^([^/]+)/|,
  471     capture => [ qw/problemID/ ],
  472     produce => '$problemID/',
  473     display => 'WeBWorK::ContentGenerator::Problem',
  474   },
  475 
  476 );
  477 
  478 =for comment
  479 
  480 a handy template:
  481 
  482   id => {
  483     name    => '',
  484     parent  => '',
  485     kids    => [ qw// ],
  486     match   => qr|^/|,
  487     capture => [ qw// ],
  488     produce => '',
  489     display => '',
  490   },
  491 
  492 =cut
  493 
  494 ################################################################################
  495 
  496 =head1 CONSTRUCTORS
  497 
  498 =over
  499 
  500 =item new(%fields)
  501 
  502 Creates a new WeBWorK::URLPath. %fields may contain the following:
  503 
  504  type => the internal path type associated with this
  505  args => a reference to a hash associating path arguments with values
  506 
  507 This constructor is used internally. Refer to newFromPath() and newFromModule()
  508 for more useful constructors.
  509 
  510 =cut
  511 
  512 sub new {
  513   my ($invocant, %fields) = @_;
  514   my $class = ref $invocant || $invocant;
  515   my $self = {
  516     type => undef,
  517     args => {},
  518     %fields,
  519   };
  520   return bless $self, $class;
  521 }
  522 
  523 =item newFromPath($path)
  524 
  525 Creates a new WeBWorK::URLPath by parsing the path given in $path. It the path
  526 is invalid, an exception is thrown.
  527 
  528 =cut
  529 
  530 sub newFromPath {
  531   my ($invocant, $path) = @_;
  532 
  533   my ($type, %args) = getPathType($path);
  534   die "no type matches path $path" unless $type;
  535 
  536   return $invocant->new(
  537     type => $type,
  538     args => \%args,
  539   );
  540 }
  541 
  542 =item newFromModule($module, %args)
  543 
  544 Creates a new WeBWorK::URLPath by finding a path type which matches the module
  545 and path arguments given. If no type matches, an exception is thrown.
  546 
  547 =cut
  548 
  549 sub newFromModule {
  550   my ($invocant, $module, %args) = @_;
  551 
  552   my $type = getModuleType($module, keys %args);
  553   die "no type matches module $module with args", map { " $_=>$args{$_}" } keys %args unless $type;
  554 
  555   return $invocant->new(
  556     type => $type,
  557     args => \%args
  558   );
  559 }
  560 
  561 =back
  562 
  563 =cut
  564 
  565 ################################################################################
  566 
  567 =head1 METHODS
  568 
  569 =head2 Methods that return information from the object itself
  570 
  571 =over
  572 
  573 =item type()
  574 
  575 Returns the path type of the WeBWorK::URLPath.
  576 
  577 =cut
  578 
  579 sub type {
  580   my ($self) = @_;
  581   my $type = $self->{type};
  582 
  583   return $type;
  584 }
  585 
  586 =item args()
  587 
  588 Returns a hash of arguments derived from the WeBWorK::URLPath.
  589 
  590 =cut
  591 
  592 sub args {
  593   my ($self) = @_;
  594   my %args = %{ $self->{args} };
  595 
  596   return %args;
  597 }
  598 
  599 =item arg($name)
  600 
  601 Returns the named argument, as derived from the WeBWorK::URLPath.
  602 
  603 =cut
  604 
  605 sub arg {
  606   my ($self, $name) = @_;
  607   my %args = %{ $self->{args} };
  608 
  609   return $args{$name};
  610 }
  611 
  612 =back
  613 
  614 =cut
  615 
  616 # ------------------------------------------------------------------------------
  617 
  618 =head2 Methods that return information from path node associated with the object
  619 
  620 =over
  621 
  622 =item name()
  623 
  624 Returns the human-readable name of this WeBWorK::URLPath.
  625 
  626 =cut
  627 
  628 sub name {
  629   my ($self) = @_;
  630   my $type = $self->{type};
  631   my %args = $self->args;
  632 
  633   my $name = $pathTypes{$type}->{name};
  634   $name = interpolate($name, %args);
  635 
  636   return $name;
  637 }
  638 
  639 =item module()
  640 
  641 Returns the name of the module that will handle this WeBWorK::URLPath.
  642 
  643 =cut
  644 
  645 sub module {
  646   my ($self) = @_;
  647   my $type = $self->{type};
  648 
  649   return $pathTypes{$type}->{display};
  650 }
  651 
  652 =back
  653 
  654 =cut
  655 
  656 # ------------------------------------------------------------------------------
  657 
  658 =head2 Methods that search the virtual heirarchy
  659 
  660 =over
  661 
  662 =item parent()
  663 
  664 Returns a new WeBWorK::URLPath representing the parent of the current URLPath.
  665 Returns an undefined value if the URLPath has no parent.
  666 
  667 =cut
  668 
  669 sub parent {
  670   my ($self) = @_;
  671   my $type = $self->{type};
  672 
  673   my $newType = $pathTypes{$self->{type}}->{parent};
  674   return undef unless $newType;
  675 
  676   # remove any arguments added by the current node (and therefore not needed by the parent)
  677   my @currArgs = @{ $pathTypes{$type}->{capture} };
  678   my %newArgs = %{ $self->{args} };
  679   delete @newArgs{@currArgs} if @currArgs;
  680 
  681   return $self->new(type => $newType, args => \%newArgs);
  682 }
  683 
  684 =item child($module, %newArgs)
  685 
  686 Returns a new WeBWorK::URLPath representing the child of the current URLPath
  687 whose module is C<$module>. If no child matches, an undefined value is returned.
  688 Pass additional arguments needed by the child in C<%newArgs>.
  689 
  690 =cut
  691 
  692 sub child {
  693   my ($self, $module, %newArgs) = @_;
  694   my $type = $self->{type};
  695 
  696   my @kids = @{ $pathTypes{$type}->{kids} };
  697   my $newType;
  698   foreach my $kid (@kids) {
  699     if ($pathTypes{$kid}->{module} eq $module) {
  700       $newType = $kid;
  701       last;
  702     }
  703   }
  704 
  705   if ($newType) {
  706     return $self->new(type => $newType, args => \%newArgs);
  707   } else {
  708     return undef;
  709   }
  710 }
  711 
  712 =item path()
  713 
  714 Reconstructs the path string from a WeBWorK::URLPath.
  715 
  716 =cut
  717 
  718 sub path {
  719   my ($self) = @_;
  720   my $type = $self->type;
  721   my %args = %{ $self->{args} };
  722 
  723   my $path = buildPathFromType($type);
  724   $path = interpolate($path, %args);
  725 
  726   return $path;
  727 }
  728 
  729 =back
  730 
  731 =cut
  732 
  733 ################################################################################
  734 
  735 =head1 UTILITY FUNCTIONS
  736 
  737 =head2
  738 
  739 =over
  740 
  741 =item interpolate($string, %symbols)
  742 
  743 Replaces simple scalars (\$\w+) in $string with values in %symbols. If a scalar
  744 does not exist in %symbols, it is left alone.
  745 
  746 =cut
  747 
  748 sub interpolate {
  749   my ($string, %symbols) = @_;
  750 
  751   $string =~ s/\$(\w+)/exists $symbols{$1} ? $symbols{$1} : "\$$1"/eg;
  752 
  753   return $string;
  754 }
  755 
  756 =back
  757 
  758 =cut
  759 
  760 # ------------------------------------------------------------------------------
  761 
  762 =head2
  763 
  764 =over
  765 
  766 =item getPathType($path)
  767 
  768 Parse the string $path, determining the path type. Returns ($type, %args), where
  769 $type is the type of the path and %args contains any extracted path arguments.
  770 If conversion fails, a false value is returned.
  771 
  772 =cut
  773 
  774 sub getPathType($) {
  775   my ($path) = @_;
  776 
  777   my %args;
  778   my $context = visitPathTypeNode("root", $path, \%args, 0);
  779 
  780   return $context, %args;
  781 }
  782 
  783 =item getModuleType($module, @args)
  784 
  785 Returns the path type matching the given module and argument names, or a false
  786 value if no type matches.
  787 
  788 =cut
  789 
  790 sub getModuleType {
  791   my ($module, @args) = @_;
  792   @args = sort @args;
  793   my %args;
  794   @args{@args} = ();
  795 
  796   NODE: foreach my $nodeID (keys %pathTypes) {
  797     my $node = $pathTypes{$nodeID};
  798 
  799     # module name matches?
  800     next NODE unless defined $node->{display} and $node->{display} eq $module;
  801 
  802     # collect all captures from here to root
  803     my @captures;
  804     my $tmpNodeID = $nodeID;
  805     while ($tmpNodeID) {
  806       my $tmpNode = $pathTypes{$tmpNodeID};
  807       push @captures, @{ $tmpNode->{capture} };
  808       $tmpNodeID = $tmpNode->{parent};
  809     }
  810 
  811     # same number of captures?
  812     next NODE unless @captures == @args;
  813 
  814     # same captures?
  815     @captures = sort @captures;
  816     for (my $i = 0; $i < @args; $i++) {
  817       next NODE unless $args[$i] eq $captures[$i];
  818     }
  819 
  820     # if we got here, this node matches
  821     return $nodeID;
  822   }
  823 
  824   return 0; # no node matches
  825 }
  826 
  827 =item buildPathFromType($type)
  828 
  829 Returns a string path for the given path type. Since arguments are not supplied,
  830 the string may contain scalar variables ripe for interpolation.
  831 
  832 =cut
  833 
  834 sub buildPathFromType($) {
  835   my ($type) = @_;
  836 
  837   my $path = "";
  838 
  839   while ($type) {
  840     $path = $pathTypes{$type}->{produce} . $path;
  841     $type = $pathTypes{$type}->{parent};
  842   };
  843 
  844   return $path;
  845 }
  846 
  847 =item visitPathTypeNode($nodeID, $path, $argsRef, $indent)
  848 
  849 Internal search function. See getPathType().
  850 
  851 Returns the nodeID of the node that consumed the final characters in $path, or
  852 the following failure conditions:
  853 
  854 Returns 0 if $nodeID doesn't match $path.
  855 
  856 Returns -1 if $nodeID matched $path, but no children of $nodeID consumed the
  857 remaining path. In this case, the stack is unwound immediately.
  858 
  859 =cut
  860 
  861 sub visitPathTypeNode($$$$);
  862 
  863 sub visitPathTypeNode($$$$) {
  864   my ($nodeID, $path, $argsRef, $indent) = @_;
  865   debug("visitPathTypeNode", $indent, "visiting node $nodeID with path $path\n");
  866 
  867   unless (exists $pathTypes{$nodeID}) {
  868     debug("visitPathTypeNode", $indent, "node $nodeID doesn't exist in node list: failed\n");
  869     die "node $nodeID doesn't exist in node list: failed";
  870   }
  871 
  872   my %node = %{ $pathTypes{$nodeID} };
  873   my $match = $node{match};
  874   my @capture_names = @{ $node{capture} };
  875 
  876   # attempt to match $path against $match.
  877   debug("visitPathTypeNode", $indent, "trying to match $match: ");
  878   if ($path =~ s/($match)//) {
  879     # it matches! store captured strings in $argsRef and remove the matched
  880     # characters from $path. waste a lot of lines on sanity checking... ;)
  881     debug("", 0, "success!\n");
  882     my @capture_values = $1 =~ m/$match/;
  883     if (@capture_names) {
  884       my $nexpected = @capture_names;
  885       my $ncaptured = @capture_values;
  886       my $max = $nexpected > $ncaptured ? $nexpected : $ncaptured;
  887       warn "captured $ncaptured arguments, expected $nexpected." unless $ncaptured == $nexpected;
  888       for (my $i = 0; $i < $max; $i++) {
  889         my $name = $capture_names[$i];
  890         my $value = $capture_values[$i];
  891         if ($i > $nexpected) {
  892           warn "captured an unexpected argument: $value -- ignoring it.";
  893           next;
  894         }
  895         if ($i > $ncaptured) {
  896           warn "expected an uncaptured argument named: $name -- ignoring it.";
  897           next;
  898         }
  899         if (exists $argsRef->{$name}) {
  900           my $old = $argsRef->{$name};
  901           warn "encountered argument $name again, old value: $old new value: $value -- replacing.";
  902         }
  903         debug("visitPathTypeNode", $indent, "setting argument $name => $value.\n");
  904         $argsRef->{$name} = $value;
  905       }
  906     }
  907   } else {
  908     # it doesn't match. bail out now with return value 0
  909     debug("", 0, "failed.\n");
  910     return 0;
  911   }
  912 
  913   ##### if we're here we matched #####
  914 
  915   # if there's no more path left, then this node is the one! return $nodeID
  916   if ($path eq "") {
  917     debug("visitPathTypeNode", $indent, "no path left, type is $nodeID\n");
  918     return $nodeID;
  919   }
  920 
  921   # otherwise, we have to send the remaining path to the node's children
  922   debug("visitPathTypeNode", $indent, "but path remains: $path\n");
  923   my @kids = @{ $node{kids} };
  924   if (@kids) {
  925     foreach my $kid (@kids) {
  926       debug("visitPathTypeNode", $indent, "trying child $kid:\n");
  927       my $result = visitPathTypeNode($kid, $path, $argsRef, $indent+1);
  928       # we return in two situations:
  929       # if $result is -1, then the kid matched but couldn't consume the rest of the path
  930       # if $result is the ID of a node, then the kid matched and consumed the rest of the path
  931       # these are all true values (assuming that "0" isn't a valid node ID), so we say:
  932       return $result if $result;
  933     }
  934     debug("visitPathTypeNode", $indent, "no children claimed the remaining path: failed.\n");
  935   } else {
  936     debug("visitPathTypeNode", $indent, "no children to claim the remaining path: failed.\n");
  937   }
  938 
  939   # in both of the above cases, we matched but couldn't provide children that
  940   # would consume the rest of the path. so we return -1, causing the whole
  941   # stack to unwind. WHEEEEEEE!
  942   return -1;
  943 }
  944 
  945 =back
  946 
  947 =cut
  948 
  949 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9