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

View of /branches/ghe3_dev/webwork2/lib/WeBWorK/URLPath.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2847 - (download) (as text) (annotate)
Wed Sep 29 16:49:30 2004 UTC (8 years, 7 months ago) by sh002i
Original Path: trunk/webwork2/lib/WeBWorK/URLPath.pm
File size: 26027 byte(s)
moved new names of some modules to URLPath. added sp2nbsp() for link
names.

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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9