[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 2896 - (download) (as text) (annotate)
Mon Oct 11 23:11:41 2004 UTC (8 years, 7 months ago) by sh002i
File size: 26391 byte(s)
added davide's filemanager

    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.18 2004/10/10 20:53:19 sh002i 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_file_manager             /$courseID/instructor/file_manager/
   69  instructor_problem_editor           /$courseID/instructor/pgProblemEditor/
   70  instructor_problem_editor_withset   /$courseID/instructor/pgProblemEditor/$setID/
   71  instructor_problem_editor_withset_withproblem
   72                                      /$courseID/instructor/pgProblemEditor/$setID/$problemID/
   73 
   74  instructor_scoring                  /$courseID/instructor/scoring/
   75  instructor_scoring_download         /$courseID/instructor/scoringDownload/
   76  instructor_mail_merge               /$courseID/instructor/send_mail/
   77  instructor_answer_log               /$courseID/instructor/show_answers/
   78  instructor_preflight               /$courseID/instructor/preflight/
   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    => '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    => 'Logout',
  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    => 'Password/Email',
  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 instructor_file_manager
  213       instructor_problem_editor instructor_set_maker
  214       instructor_scoring instructor_scoring_download instructor_mail_merge
  215       instructor_answer_log instructor_preflight 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    => 'Classlist Editor',
  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    => 'Hmwk Sets Editor',
  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    => 'Set Detail for set $setID',
  267     parent  => 'instructor_set_list',
  268     kids    => [ qw/instructor_users_assigned_to_set/ ],
  269     match   => qr|^([^/]+)/|,
  270     capture => [ qw/setID/ ],
  271     produce => '$setID/',
  272     display => 'WeBWorK::ContentGenerator::Instructor::ProblemSetDetail',
  273   },
  274   instructor_users_assigned_to_set => {
  275     name    => 'Users Assigned to Set',
  276     parent  => 'instructor_set_detail',
  277     kids    => [ qw// ],
  278     match   => qr|^users/|,
  279     capture => [ qw// ],
  280     produce => 'users/',
  281     display => 'WeBWorK::ContentGenerator::Instructor::UsersAssignedToSet',
  282   },
  283 
  284   ################################################################################
  285 
  286   instructor_add_users => {
  287     name    => 'Add Users',
  288     parent  => 'instructor_tools',
  289     kids    => [ qw// ],
  290     match   => qr|^add_users/|,
  291     capture => [ qw// ],
  292     produce => 'add_users/',
  293     display => 'WeBWorK::ContentGenerator::Instructor::AddUsers',
  294   },
  295   instructor_set_assigner => {
  296     name    => 'Set Assigner',
  297     parent  => 'instructor_tools',
  298     kids    => [ qw// ],
  299     match   => qr|^assigner/|,
  300     capture => [ qw// ],
  301     produce => 'assigner/',
  302     display => 'WeBWorK::ContentGenerator::Instructor::Assigner',
  303   },
  304   instructor_set_maker => {
  305     name    => 'Library Browser',
  306     parent  => 'instructor_tools',
  307     kids    => [ qw// ],
  308     match   => qr|^setmaker/|,
  309     capture => [ qw// ],
  310     produce => 'setmaker/',
  311     display => 'WeBWorK::ContentGenerator::Instructor::SetMaker',
  312   },
  313   instructor_file_transfer => {
  314     name    => 'File Transfer',
  315     parent  => 'instructor_tools',
  316     kids    => [ qw// ],
  317     match   => qr|^files/|,
  318     capture => [ qw// ],
  319     produce => 'files/',
  320     display => 'WeBWorK::ContentGenerator::Instructor::FileXfer',
  321   },
  322   instructor_problem_editor => {
  323     name    => 'Problem Editor',
  324     parent  => 'instructor_tools',
  325     kids    => [ qw/instructor_problem_editor_withset/ ],
  326     match   => qr|^pgProblemEditor/|,
  327     capture => [ qw// ],
  328     produce => 'pgProblemEditor/',
  329     display => 'WeBWorK::ContentGenerator::Instructor::PGProblemEditor',
  330   },
  331   instructor_file_manager => {
  332     name    => 'File Manager',
  333     parent  => 'instructor_tools',
  334     kids    => [ qw// ],
  335     match   => qr|^file_manager/|,
  336     capture => [ qw// ],
  337     produce => 'file_manager/',
  338     display => 'WeBWorK::ContentGenerator::Instructor::FileManager',
  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    => 'Email',
  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   instructor_preflight => {
  395     name    => 'Preflight Log',
  396     parent  => 'instructor_tools',
  397     kids    => [ qw// ],
  398     match   => qr|^preflight/|,
  399     capture => [ qw// ],
  400     produce => 'preflight/',
  401     display => 'WeBWorK::ContentGenerator::Instructor::Preflight',
  402   },
  403 
  404   ################################################################################
  405 
  406   instructor_statistics => {
  407     name    => 'Statistics',
  408     parent  => 'instructor_tools',
  409     kids    => [ qw/instructor_set_statistics instructor_user_statistics/ ],
  410     match   => qr|^stats/|,
  411     capture => [ qw// ],
  412     produce => 'stats/',
  413     display => 'WeBWorK::ContentGenerator::Instructor::Stats',
  414   },
  415   instructor_set_statistics => {
  416     name    => 'Statistics',
  417     parent  => 'instructor_statistics',
  418     kids    => [ qw// ],
  419     match   => qr|^(set)/([^/]+)/|,
  420     capture => [ qw/statType setID/ ],
  421     produce => 'set/$setID/',
  422     display => 'WeBWorK::ContentGenerator::Instructor::Stats',
  423   },
  424   instructor_user_statistics => {
  425     name    => 'Statistics',
  426     parent  => 'instructor_statistics',
  427     kids    => [ qw// ],
  428     match   => qr|^(student)/([^/]+)/|,
  429     capture => [ qw/statType userID/ ],
  430     produce => 'student/$userID/',
  431     display => 'WeBWorK::ContentGenerator::Instructor::Stats',
  432   },
  433 
  434   ################################################################################
  435 
  436   instructor_progress => {
  437     name    => 'Student Progress',
  438     parent  => 'instructor_tools',
  439     kids    => [ qw/instructor_set_progress instructor_user_progress/ ],
  440     match   => qr|^progress/|,
  441     capture => [ qw// ],
  442     produce => 'progress/',
  443     display => 'WeBWorK::ContentGenerator::Instructor::StudentProgress',
  444   },
  445   instructor_set_progress => {
  446     name    => 'Student Progress',
  447     parent  => 'instructor_progress',
  448     kids    => [ qw// ],
  449     match   => qr|^(set)/([^/]+)/|,
  450     capture => [ qw/statType setID/ ],
  451     produce => 'set/$setID/',
  452     display => 'WeBWorK::ContentGenerator::Instructor::StudentProgress',
  453   },
  454   instructor_user_progress => {
  455     name    => 'Student Progress',
  456     parent  => 'instructor_progress',
  457     kids    => [ qw// ],
  458     match   => qr|^(student)/([^/]+)/|,
  459     capture => [ qw/statType userID/ ],
  460     produce => 'student/$userID/',
  461     display => 'WeBWorK::ContentGenerator::Instructor::StudentProgress',
  462   },
  463 
  464   ################################################################################
  465 
  466   problem_list => {
  467     name    => '$setID',
  468     parent  => 'set_list',
  469     kids    => [ qw/problem_detail/ ],
  470     match   => qr|^([^/]+)/|,
  471     capture => [ qw/setID/ ],
  472     produce => '$setID/',
  473     display => 'WeBWorK::ContentGenerator::ProblemSet',
  474   },
  475   problem_detail => {
  476     name    => '$problemID',
  477     parent  => 'problem_list',
  478     kids    => [ qw// ],
  479     match   => qr|^([^/]+)/|,
  480     capture => [ qw/problemID/ ],
  481     produce => '$problemID/',
  482     display => 'WeBWorK::ContentGenerator::Problem',
  483   },
  484 
  485 );
  486 
  487 =for comment
  488 
  489 a handy template:
  490 
  491   id => {
  492     name    => '',
  493     parent  => '',
  494     kids    => [ qw// ],
  495     match   => qr|^/|,
  496     capture => [ qw// ],
  497     produce => '',
  498     display => '',
  499   },
  500 
  501 =cut
  502 
  503 ################################################################################
  504 
  505 =head1 CONSTRUCTORS
  506 
  507 =over
  508 
  509 =item new(%fields)
  510 
  511 Creates a new WeBWorK::URLPath. %fields may contain the following:
  512 
  513  type => the internal path type associated with this
  514  args => a reference to a hash associating path arguments with values
  515 
  516 This constructor is used internally. Refer to newFromPath() and newFromModule()
  517 for more useful constructors.
  518 
  519 =cut
  520 
  521 sub new {
  522   my ($invocant, %fields) = @_;
  523   my $class = ref $invocant || $invocant;
  524   my $self = {
  525     type => undef,
  526     args => {},
  527     %fields,
  528   };
  529   return bless $self, $class;
  530 }
  531 
  532 =item newFromPath($path)
  533 
  534 Creates a new WeBWorK::URLPath by parsing the path given in $path. It the path
  535 is invalid, an exception is thrown.
  536 
  537 =cut
  538 
  539 sub newFromPath {
  540   my ($invocant, $path) = @_;
  541 
  542   my ($type, %args) = getPathType($path);
  543   die "no type matches path $path" unless $type;
  544 
  545   return $invocant->new(
  546     type => $type,
  547     args => \%args,
  548   );
  549 }
  550 
  551 =item newFromModule($module, %args)
  552 
  553 Creates a new WeBWorK::URLPath by finding a path type which matches the module
  554 and path arguments given. If no type matches, an exception is thrown.
  555 
  556 =cut
  557 
  558 sub newFromModule {
  559   my ($invocant, $module, %args) = @_;
  560 
  561   my $type = getModuleType($module, keys %args);
  562   die "no type matches module $module with args", map { " $_=>$args{$_}" } keys %args unless $type;
  563 
  564   return $invocant->new(
  565     type => $type,
  566     args => \%args
  567   );
  568 }
  569 
  570 =back
  571 
  572 =cut
  573 
  574 ################################################################################
  575 
  576 =head1 METHODS
  577 
  578 =head2 Methods that return information from the object itself
  579 
  580 =over
  581 
  582 =item type()
  583 
  584 Returns the path type of the WeBWorK::URLPath.
  585 
  586 =cut
  587 
  588 sub type {
  589   my ($self) = @_;
  590   my $type = $self->{type};
  591 
  592   return $type;
  593 }
  594 
  595 =item args()
  596 
  597 Returns a hash of arguments derived from the WeBWorK::URLPath.
  598 
  599 =cut
  600 
  601 sub args {
  602   my ($self) = @_;
  603   my %args = %{ $self->{args} };
  604 
  605   return %args;
  606 }
  607 
  608 =item arg($name)
  609 
  610 Returns the named argument, as derived from the WeBWorK::URLPath.
  611 
  612 =cut
  613 
  614 sub arg {
  615   my ($self, $name) = @_;
  616   my %args = %{ $self->{args} };
  617 
  618   return $args{$name};
  619 }
  620 
  621 =back
  622 
  623 =cut
  624 
  625 # ------------------------------------------------------------------------------
  626 
  627 =head2 Methods that return information from path node associated with the object
  628 
  629 =over
  630 
  631 =item name()
  632 
  633 Returns the human-readable name of this WeBWorK::URLPath.
  634 
  635 =cut
  636 
  637 sub name {
  638   my ($self) = @_;
  639   my $type = $self->{type};
  640   my %args = $self->args;
  641 
  642   my $name = $pathTypes{$type}->{name};
  643   $name = interpolate($name, %args);
  644 
  645   return $name;
  646 }
  647 
  648 =item module()
  649 
  650 Returns the name of the module that will handle this WeBWorK::URLPath.
  651 
  652 =cut
  653 
  654 sub module {
  655   my ($self) = @_;
  656   my $type = $self->{type};
  657 
  658   return $pathTypes{$type}->{display};
  659 }
  660 
  661 =back
  662 
  663 =cut
  664 
  665 # ------------------------------------------------------------------------------
  666 
  667 =head2 Methods that search the virtual heirarchy
  668 
  669 =over
  670 
  671 =item parent()
  672 
  673 Returns a new WeBWorK::URLPath representing the parent of the current URLPath.
  674 Returns an undefined value if the URLPath has no parent.
  675 
  676 =cut
  677 
  678 sub parent {
  679   my ($self) = @_;
  680   my $type = $self->{type};
  681 
  682   my $newType = $pathTypes{$self->{type}}->{parent};
  683   return undef unless $newType;
  684 
  685   # remove any arguments added by the current node (and therefore not needed by the parent)
  686   my @currArgs = @{ $pathTypes{$type}->{capture} };
  687   my %newArgs = %{ $self->{args} };
  688   delete @newArgs{@currArgs} if @currArgs;
  689 
  690   return $self->new(type => $newType, args => \%newArgs);
  691 }
  692 
  693 =item child($module, %newArgs)
  694 
  695 Returns a new WeBWorK::URLPath representing the child of the current URLPath
  696 whose module is C<$module>. If no child matches, an undefined value is returned.
  697 Pass additional arguments needed by the child in C<%newArgs>.
  698 
  699 =cut
  700 
  701 sub child {
  702   my ($self, $module, %newArgs) = @_;
  703   my $type = $self->{type};
  704 
  705   my @kids = @{ $pathTypes{$type}->{kids} };
  706   my $newType;
  707   foreach my $kid (@kids) {
  708     if ($pathTypes{$kid}->{module} eq $module) {
  709       $newType = $kid;
  710       last;
  711     }
  712   }
  713 
  714   if ($newType) {
  715     return $self->new(type => $newType, args => \%newArgs);
  716   } else {
  717     return undef;
  718   }
  719 }
  720 
  721 =item path()
  722 
  723 Reconstructs the path string from a WeBWorK::URLPath.
  724 
  725 =cut
  726 
  727 sub path {
  728   my ($self) = @_;
  729   my $type = $self->type;
  730   my %args = %{ $self->{args} };
  731 
  732   my $path = buildPathFromType($type);
  733   $path = interpolate($path, %args);
  734 
  735   return $path;
  736 }
  737 
  738 =back
  739 
  740 =cut
  741 
  742 ################################################################################
  743 
  744 =head1 UTILITY FUNCTIONS
  745 
  746 =head2
  747 
  748 =over
  749 
  750 =item interpolate($string, %symbols)
  751 
  752 Replaces simple scalars (\$\w+) in $string with values in %symbols. If a scalar
  753 does not exist in %symbols, it is left alone.
  754 
  755 =cut
  756 
  757 sub interpolate {
  758   my ($string, %symbols) = @_;
  759 
  760   $string =~ s/\$(\w+)/exists $symbols{$1} ? $symbols{$1} : "\$$1"/eg;
  761 
  762   return $string;
  763 }
  764 
  765 =back
  766 
  767 =cut
  768 
  769 # ------------------------------------------------------------------------------
  770 
  771 =head2
  772 
  773 =over
  774 
  775 =item getPathType($path)
  776 
  777 Parse the string $path, determining the path type. Returns ($type, %args), where
  778 $type is the type of the path and %args contains any extracted path arguments.
  779 If conversion fails, a false value is returned.
  780 
  781 =cut
  782 
  783 sub getPathType($) {
  784   my ($path) = @_;
  785 
  786   my %args;
  787   my $context = visitPathTypeNode("root", $path, \%args, 0);
  788 
  789   return $context, %args;
  790 }
  791 
  792 =item getModuleType($module, @args)
  793 
  794 Returns the path type matching the given module and argument names, or a false
  795 value if no type matches.
  796 
  797 =cut
  798 
  799 sub getModuleType {
  800   my ($module, @args) = @_;
  801   @args = sort @args;
  802   my %args;
  803   @args{@args} = ();
  804 
  805   NODE: foreach my $nodeID (keys %pathTypes) {
  806     my $node = $pathTypes{$nodeID};
  807 
  808     # module name matches?
  809     next NODE unless defined $node->{display} and $node->{display} eq $module;
  810 
  811     # collect all captures from here to root
  812     my @captures;
  813     my $tmpNodeID = $nodeID;
  814     while ($tmpNodeID) {
  815       my $tmpNode = $pathTypes{$tmpNodeID};
  816       push @captures, @{ $tmpNode->{capture} };
  817       $tmpNodeID = $tmpNode->{parent};
  818     }
  819 
  820     # same number of captures?
  821     next NODE unless @captures == @args;
  822 
  823     # same captures?
  824     @captures = sort @captures;
  825     for (my $i = 0; $i < @args; $i++) {
  826       next NODE unless $args[$i] eq $captures[$i];
  827     }
  828 
  829     # if we got here, this node matches
  830     return $nodeID;
  831   }
  832 
  833   return 0; # no node matches
  834 }
  835 
  836 =item buildPathFromType($type)
  837 
  838 Returns a string path for the given path type. Since arguments are not supplied,
  839 the string may contain scalar variables ripe for interpolation.
  840 
  841 =cut
  842 
  843 sub buildPathFromType($) {
  844   my ($type) = @_;
  845 
  846   my $path = "";
  847 
  848   while ($type) {
  849     $path = $pathTypes{$type}->{produce} . $path;
  850     $type = $pathTypes{$type}->{parent};
  851   };
  852 
  853   return $path;
  854 }
  855 
  856 =item visitPathTypeNode($nodeID, $path, $argsRef, $indent)
  857 
  858 Internal search function. See getPathType().
  859 
  860 Returns the nodeID of the node that consumed the final characters in $path, or
  861 the following failure conditions:
  862 
  863 Returns 0 if $nodeID doesn't match $path.
  864 
  865 Returns -1 if $nodeID matched $path, but no children of $nodeID consumed the
  866 remaining path. In this case, the stack is unwound immediately.
  867 
  868 =cut
  869 
  870 sub visitPathTypeNode($$$$);
  871 
  872 sub visitPathTypeNode($$$$) {
  873   my ($nodeID, $path, $argsRef, $indent) = @_;
  874   debug("visitPathTypeNode", $indent, "visiting node $nodeID with path $path\n");
  875 
  876   unless (exists $pathTypes{$nodeID}) {
  877     debug("visitPathTypeNode", $indent, "node $nodeID doesn't exist in node list: failed\n");
  878     die "node $nodeID doesn't exist in node list: failed";
  879   }
  880 
  881   my %node = %{ $pathTypes{$nodeID} };
  882   my $match = $node{match};
  883   my @capture_names = @{ $node{capture} };
  884 
  885   # attempt to match $path against $match.
  886   debug("visitPathTypeNode", $indent, "trying to match $match: ");
  887   if ($path =~ s/($match)//) {
  888     # it matches! store captured strings in $argsRef and remove the matched
  889     # characters from $path. waste a lot of lines on sanity checking... ;)
  890     debug("", 0, "success!\n");
  891     my @capture_values = $1 =~ m/$match/;
  892     if (@capture_names) {
  893       my $nexpected = @capture_names;
  894       my $ncaptured = @capture_values;
  895       my $max = $nexpected > $ncaptured ? $nexpected : $ncaptured;
  896       warn "captured $ncaptured arguments, expected $nexpected." unless $ncaptured == $nexpected;
  897       for (my $i = 0; $i < $max; $i++) {
  898         my $name = $capture_names[$i];
  899         my $value = $capture_values[$i];
  900         if ($i > $nexpected) {
  901           warn "captured an unexpected argument: $value -- ignoring it.";
  902           next;
  903         }
  904         if ($i > $ncaptured) {
  905           warn "expected an uncaptured argument named: $name -- ignoring it.";
  906           next;
  907         }
  908         if (exists $argsRef->{$name}) {
  909           my $old = $argsRef->{$name};
  910           warn "encountered argument $name again, old value: $old new value: $value -- replacing.";
  911         }
  912         debug("visitPathTypeNode", $indent, "setting argument $name => $value.\n");
  913         $argsRef->{$name} = $value;
  914       }
  915     }
  916   } else {
  917     # it doesn't match. bail out now with return value 0
  918     debug("", 0, "failed.\n");
  919     return 0;
  920   }
  921 
  922   ##### if we're here we matched #####
  923 
  924   # if there's no more path left, then this node is the one! return $nodeID
  925   if ($path eq "") {
  926     debug("visitPathTypeNode", $indent, "no path left, type is $nodeID\n");
  927     return $nodeID;
  928   }
  929 
  930   # otherwise, we have to send the remaining path to the node's children
  931   debug("visitPathTypeNode", $indent, "but path remains: $path\n");
  932   my @kids = @{ $node{kids} };
  933   if (@kids) {
  934     foreach my $kid (@kids) {
  935       debug("visitPathTypeNode", $indent, "trying child $kid:\n");
  936       my $result = visitPathTypeNode($kid, $path, $argsRef, $indent+1);
  937       # we return in two situations:
  938       # if $result is -1, then the kid matched but couldn't consume the rest of the path
  939       # if $result is the ID of a node, then the kid matched and consumed the rest of the path
  940       # these are all true values (assuming that "0" isn't a valid node ID), so we say:
  941       return $result if $result;
  942     }
  943     debug("visitPathTypeNode", $indent, "no children claimed the remaining path: failed.\n");
  944   } else {
  945     debug("visitPathTypeNode", $indent, "no children to claim the remaining path: failed.\n");
  946   }
  947 
  948   # in both of the above cases, we matched but couldn't provide children that
  949   # would consume the rest of the path. so we return -1, causing the whole
  950   # stack to unwind. WHEEEEEEE!
  951   return -1;
  952 }
  953 
  954 =back
  955 
  956 =cut
  957 
  958 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9