[system] / trunk / webwork-modperl / lib / WeBWorK / URLPath.pm Repository:
ViewVC logotype

View of /trunk/webwork-modperl/lib/WeBWorK/URLPath.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2238 - (download) (as text) (annotate)
Tue Jun 1 14:44:17 2004 UTC (8 years, 11 months ago) by gage
File size: 26459 byte(s)
Added hooks for Preflight

-- Mike

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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9