1package Geo::Postcodes; 2 3################################################################################# 4# # 5# This file is written by Arne Sommer - perl@bbop.org # 6# # 7################################################################################# 8 9use strict; 10use warnings; 11 12our $VERSION = '0.32'; 13 14## Which methods are available ################################################## 15 16my @valid_fields = qw(postcode location borough county type type_verbose owner 17 address); # Used by the 'get_fields' procedure. 18 19my %valid_fields; 20 21foreach (@valid_fields) 22{ 23 $valid_fields{$_} = 1; # Used by 'is_field' for easy lookup. 24} 25 26## Type Description ############################################################# 27 28my %typedesc; 29 30$typedesc{BX} = "Post Office box"; 31$typedesc{ST} = "Street address"; 32$typedesc{SX} = "Service box"; 33$typedesc{IO} = "Individual owner"; 34$typedesc{STBX} = "Street Address and Post Office box"; 35$typedesc{MU} = "Multiple usage"; 36$typedesc{PP} = "Porto Paye receiver"; 37 38$typedesc{PN} = "Place name"; 39 40## OO Methods ################################################################### 41 42our %postcode_of; 43our %location_of; 44our %borough_of; 45our %county_of; 46our %type_of; 47our %owner_of; 48our %address_of; 49 50sub new 51{ 52 my $class = shift; 53 my $postcode = shift; 54 my $self = shift; # Allow for subclassing. 55 56 return unless valid($postcode); 57 58 unless ($self) 59 { 60 $self = bless \(my $dummy), $class; 61 } 62 63 $postcode_of {$self} = $postcode; 64 $location_of {$self} = location_of ($postcode); 65 $borough_of {$self} = borough_of ($postcode); 66 $county_of {$self} = county_of ($postcode); 67 $type_of {$self} = type_of ($postcode); 68 $owner_of {$self} = owner_of ($postcode); 69 $address_of {$self} = address_of ($postcode); 70 return $self; 71} 72 73sub DESTROY 74{ 75 my $object_id = $_[0]; 76 77 delete $postcode_of {$object_id}; 78 delete $location_of {$object_id}; 79 delete $borough_of {$object_id}; 80 delete $county_of {$object_id}; 81 delete $type_of {$object_id}; 82 delete $owner_of {$object_id}; 83 delete $address_of {$object_id}; 84} 85 86sub postcode 87{ 88 my $self = shift; 89 return unless defined $self; 90 return $postcode_of{$self} if exists $postcode_of{$self}; 91 return; 92} 93 94sub location 95{ 96 my $self = shift; 97 return unless defined $self; 98 return $location_of{$self} if exists $location_of{$self}; 99 return; 100} 101 102sub borough 103{ 104 my $self = shift; 105 return unless defined $self; 106 return $borough_of{$self} if exists $borough_of{$self}; 107 return; 108} 109 110sub county 111{ 112 my $self = shift; 113 return unless defined $self; 114 return $county_of{$self} if exists $county_of{$self}; 115 return; 116} 117 118sub type 119{ 120 my $self = shift; 121 return unless defined $self; 122 return $type_of{$self} if exists $type_of{$self}; 123 return; 124} 125 126sub type_verbose 127{ 128 my $self = shift; 129 return unless defined $self; 130 return unless exists $type_of{$self}; 131 return unless exists $typedesc{$type_of{$self}}; 132 return $typedesc{$type_of{$self}}; 133} 134 135sub owner 136{ 137 my $self = shift; 138 return unless defined $self; 139 return $owner_of{$self} if exists $owner_of{$self}; 140 return; 141} 142 143sub address 144{ 145 my $self = shift; 146 return unless defined $self; 147 return $address_of{$self} if exists $address_of{$self}; 148 return; 149} 150 151################################################################################# 152 153sub get_postcodes ## Return all the postcodes, unsorted. 154{ 155 return; 156} 157 158sub get_fields ## Get a list of legal fields for the class/object. 159{ 160 return @valid_fields; 161} 162 163sub is_field ## Is the specified field legal? Can be called as 164{ ## a procedure, or as a method. 165 my $field = shift; 166 $field = shift if $field =~ /Geo::Postcodes/; # Called on an object. 167 168 return 1 if $valid_fields{$field}; 169 return 0; 170} 171 172## Global Procedures - Stub Version, Override in your subclass ################# 173 174sub legal # Is it a legal code, i.e. something that follows the syntax rule. 175{ 176 return 0; 177} 178 179sub valid # Is the code in actual use. 180{ 181 return 0; 182} 183 184sub postcode_of 185{ 186 return; 187} 188 189sub location_of 190{ 191 return; 192} 193 194sub borough_of 195{ 196 return; 197} 198 199sub county_of 200{ 201 return; 202} 203 204sub type_of 205{ 206 return; 207} 208 209sub type_verbose_of 210{ 211 return; 212} 213 214sub owner_of 215{ 216 return; 217} 218 219sub address_of 220{ 221 return; 222} 223 224sub get_types 225{ 226 return keys %typedesc; 227} 228 229sub type2verbose 230{ 231 my $type = shift; 232 return unless $type; 233 return unless exists $typedesc{$type}; 234 return $typedesc{$type}; 235} 236 237my %legal_mode; 238 $legal_mode{'and'} = $legal_mode{'and not'} = 1; 239 $legal_mode{'nand'} = $legal_mode{'nand not'} = 1; 240 $legal_mode{'nor'} = $legal_mode{'nor not'} = 1; 241 $legal_mode{'or'} = $legal_mode{'or not'} = 1; 242 $legal_mode{'xnor'} = $legal_mode{'xnor not'} = 1; 243 $legal_mode{'xor'} = $legal_mode{'xor not'} = 1; 244 245my %legal_initial_mode; 246 $legal_initial_mode{'all'} = $legal_initial_mode{'none'} = 1; 247 $legal_initial_mode{'not'} = $legal_initial_mode{'one'} = 1; 248 249sub is_legal_selectionmode 250{ 251 my $mode = shift; 252 return 1 if $legal_mode{$mode}; 253 return 0; 254} 255 256sub is_legal_initial_selectionmode 257{ 258 my $mode = shift; 259 return 1 if $legal_initial_mode{$mode} or $legal_mode{$mode}; 260 return 0; 261} 262 263sub get_selectionmodes 264{ 265 return sort keys %legal_mode; 266} 267 268sub get_initial_selectionmodes 269{ 270 return sort (keys %legal_mode, keys %legal_initial_mode); 271} 272 273sub verify_selectionlist 274{ 275 return Geo::Postcodes::_verify_selectionlist('Geo::Postcodes', @_); 276 # Black magic. 277} 278 279sub _verify_selectionlist 280{ 281 my $caller_class = shift; 282 my @args = @_; # A list of selection arguments to verify 283 284 my $status = 1; # Return value 285 my @out = (); 286 my @verbose = (); 287 288 return (0, "No arguments") unless @args; 289 290 if (is_legal_initial_selectionmode($args[0])) 291 { 292 my $mode = shift @args; 293 294 if (@args and $args[0] eq "not" and is_legal_initial_selectionmode("$mode $args[0]")) 295 { 296 $mode = "$mode $args[0]"; 297 shift @args; 298 } 299 300 push @out, $mode; 301 push @verbose, "Mode: '$mode' - ok"; 302 303 return (1, @out) if $mode eq "all" or $mode eq "none"; 304 return (1, @out) if $mode eq "one" and @args == 0; 305 # This one can both be used alone, or followed by more. 306 307 return (0, @verbose, "Missing method/value pair - not ok") unless @args >= 2; 308 # Missing method/value pair. 309 } 310 311 ## Done with the first one 312 313 while (@args) 314 { 315 my $argument = shift(@args); 316 317 if ($caller_class->is_field($argument)) 318 { 319 push @out, $argument; 320 push @verbose, "Field: '$argument' - ok"; 321 322 if (@args) 323 { 324 $argument = shift(@args); 325 push @out, $argument; 326 push @verbose, "String: '$argument' - ok"; 327 } 328 else 329 { 330 push @verbose, "Missing string - not ok"; # The last element was a method. 331 $status = 0; 332 @args = (); # Terminate the loop 333 } 334 } 335 elsif (is_legal_selectionmode($argument)) 336 { 337 if (@args and $args[0] eq "not" and is_legal_selectionmode("$argument $args[0]")) 338 { 339 $argument = "$argument $args[0]"; 340 shift @args; 341 } 342 push @out, $argument; 343 push @verbose, "Mode: '$argument' - ok"; 344 345 unless (@args >= 2) # Missing method/value pair 346 { 347 push @verbose, "Missing method/value pair - not ok"; 348 $status = 0; 349 @args = (); # Terminate the loop 350 } 351 } 352 elsif ($argument eq 'procedure') 353 { 354 push @out, $argument; 355 push @verbose, "Field: 'procedure' - ok"; 356 357 my $procedure = shift(@args); 358 if (ref $procedure eq "CODE") 359 { 360 if (_valid_procedure_pointer($procedure)) 361 { 362 push @out, $procedure; 363 push @verbose, "Procedure pointer: '$procedure' - ok"; 364 } 365 else 366 { 367 push @verbose, "No such procedure: '$procedure' - not ok"; 368 $status = 0; 369 @args = (); # Terminate the loop 370 } 371 } 372 else 373 { 374 push @verbose, "Not a procedure pointer: '$procedure' - not ok"; 375 $status = 0; 376 @args = (); # Terminate the loop 377 } 378 } 379 else 380 { 381 push @verbose, "Illegal argument: '$argument' - not ok"; 382 $status = 0; 383 @args = (); # Terminate the loop 384 } 385 } 386 387 return (1, @out) if $status; # Return a modified argument list on success. 388 389 return (0, @verbose); # Return a list of diagnostic meddages on failure. 390} 391 392sub selection_loop 393{ 394 return Geo::Postcodes::_selection_loop('Geo::Postcodes', @_); 395 # Black magic. 396} 397 398sub _selection_loop 399{ 400 my $caller_class = shift; 401 402 my $objects_requested = 0; # Not object oriented. 403 404 if ($_[0] eq $caller_class) 405 { 406 $objects_requested = 1; 407 shift; 408 } 409 410 my $procedure_pointer = shift; 411 412 return 0 unless $procedure_pointer; 413 414 my @selection_clauses = @_; 415 my @postcodes = _selection($caller_class, @selection_clauses); 416 417 return 0 unless @postcodes; 418 419 foreach (@postcodes) 420 { 421 &$procedure_pointer($objects_requested ? $caller_class->new($_) : $_); 422 } 423 return 1; 424} 425 426 427################################################################################# 428# # 429# Returns a list of postcodes if called as a procedure; # 430# Geo::Postcodes::XX::selection(...) # 431# Returns a list of objects if called as a method; # 432# Geo::Postcodes::XX->selection(...) # 433# # 434# Note that 'or' and 'not' are not written efficient, as they recompile the # 435# regular expression(s) for every postcode. # 436# # 437################################################################################# 438 439sub selection 440{ 441 return Geo::Postcodes::_selection('Geo::Postcodes', @_); 442 # Black magic. 443} 444 445sub _selection 446{ 447 my $caller_class = shift; 448 449 my $objects_requested = 0; # Not object oriented. 450 451 if ($_[0] eq $caller_class) 452 { 453 $objects_requested = 1; 454 shift; 455 } 456 457 if ($_[0] eq 'all') 458 { 459 my @all = sort &{&_proc_pointer($caller_class . '::get_postcodes')}(); 460 # Get all the postcodes. 461 462 return @all unless $objects_requested; 463 464 my @out_objects; 465 466 foreach my $postcode (@all) 467 { 468 push(@out_objects, $caller_class->new($postcode)); 469 } 470 471 return @out_objects; 472 } 473 474 elsif ($_[0] eq 'none') 475 { 476 return; # Absolutely nothing. 477 } 478 479 my $limit = 0; # Set to one if we have requested only one postcode. 480 if ($_[0] eq "one") 481 { 482 $limit = 1; 483 shift; # Get rid of the mode. 484 } 485 486 my $mode = "and"; 487 # The mode defaults to 'and' unless specified. 488 489 my %out = (); 490 491 ## The first set of method/value ############################################## 492 493 my @all = &{&_proc_pointer($caller_class . '::get_postcodes')}(); 494 # Get all the postcodes. 495 496 my($field, $current_field, $value, $current_value); 497 498 if (@_) # As 'one' can be without additional arguments. 499 { 500 if (is_legal_initial_selectionmode($_[0])) 501 { 502 if ($_[1] eq "not" and is_legal_initial_selectionmode("$_[0] $_[1]")) 503 { 504 $mode = shift; $mode .= " "; $mode .= shift; 505 } 506 else 507 { 508 $mode = shift if is_legal_initial_selectionmode($_[0]); 509 } 510 } 511 512 $field = shift; 513 514 if ($field eq 'procedure') 515 { 516 my $procedure = shift; 517 return unless _valid_procedure_pointer($procedure); 518 519 my $match; 520 521 foreach my $postcode (@all) 522 { 523 eval { $match = $procedure->($_); }; 524 return if $@; # Return if the procedure was uncallable. 525 526 if ($mode =~ /not/) { $out{$postcode}++ unless $match; } 527 else { $out{$postcode}++ if $match; } 528 } 529 } 530 else 531 { 532 return unless &{&_proc_pointer($caller_class . '::is_field')}($field); 533 # Return if the specified method is undefined for the class. 534 # As and 'and' with a list with one undefined item gives an empty list. 535 536 my $current_field = &_proc_pointer($caller_class . '::' . $field .'_of'); 537 538 $value = shift; $value =~ s/%/\.\*/g; 539 return unless $value; 540 # A validity check is impossible, so this is the next best thing. 541 542 foreach my $postcode (@all) 543 { 544 $current_value = $current_field->($postcode); 545 # Call the procedure with the current postcode as argument 546 547 next unless $current_value; 548 # Skip postcodes without this field. 549 550 my $match = $current_value =~ m{^$value$}i; ## Case insensitive 551 552 if ($mode =~ /not/) { $out{$postcode}++ unless $match; } 553 else { $out{$postcode}++ if $match; } 554 } 555 } 556 557 $mode = 'and' if $mode eq 'not'; 558 } 559 560 elsif ($limit) # just one argument; 'one'. 561 { 562 map { $out{$_} = 1 } @all 563 } 564 565 while (@_) 566 { 567 if (is_legal_selectionmode($_[0])) 568 { 569 if ($_[1] eq "not" and is_legal_selectionmode("$_[0] $_[1]")) 570 { 571 $mode = shift; $mode .= " "; $mode .= shift; 572 } 573 else 574 { 575 $mode = shift if is_legal_selectionmode($_[0]); 576 } 577 } 578 579 # Use the one already on hand, if none is given. 580 581 my $is_procedure = 0; 582 my $procedure; 583 584 $field = shift; 585 586 if ($field eq 'procedure') 587 { 588 $is_procedure = 1; 589 $procedure = shift; 590 return unless _valid_procedure_pointer($procedure); 591 } 592 else 593 { 594 return unless &{&_proc_pointer($caller_class . '::is_field')}($field); 595 # Return if the specified method is undefined for the class. 596 # As an 'and' with a list with one undefined item gives an empty list. 597 598 $current_field = &_proc_pointer($caller_class . '::' . $field .'_of'); 599 600 $value = shift; 601 $value =~ s/%/\.\*/g; 602 return unless $value; 603 # A validity check is impossible, so this is the next best thing. 604 } 605 606 foreach my $postcode ($mode =~ /and/ ? (keys %out) : @all) 607 { 608 # We start with the result from the previous iteration if the mode 609 # is one of the 'and'-family. Otherwise it is one of the 'or'-family, 610 # and we have to start from scratch (@all). 611 612 my $match; 613 614 if ($procedure) 615 { 616 eval { $match = $procedure->($postcode); }; 617 return if $@; # Return if the procedure was uncallable. 618 } 619 else 620 { 621 $current_value = $current_field->($postcode); 622 # Call the procedure with the current postcode as argument 623 624 next unless $current_value; 625 # Skip postcodes without this field. 626 627 $match = $current_value =~ m{^$value$}i; ## Case insensitive 628 } 629 630 if ($mode eq "and") 631 { 632 delete $out{$postcode} unless $match; 633 } 634 elsif ($mode eq "and not") 635 { 636 delete $out{$postcode} if $match; 637 } 638 639 elsif ($mode eq "nand") 640 { 641 if ($match and $out{$postcode}) { delete $out{$postcode} if $out{$postcode}; } 642 else { $out{$postcode}++; } 643 } 644 elsif ($mode eq "nand not") 645 { 646 if (!$match and $out{$postcode}) { delete $out{$postcode} if $out{$postcode}; } 647 else { $out{$postcode}++; } 648 } 649 650 elsif ($mode eq "or") 651 { 652 $out{$postcode}++ if $match; 653 } 654 elsif ($mode eq "or not") 655 { 656 $out{$postcode}++ unless $match; 657 } 658 elsif ($mode eq "nor") 659 { 660 if (!$match and !$out{$postcode}) { $out{$postcode}++; } 661 else { delete $out{$postcode} if $out{$postcode}; } 662 } 663 elsif ($mode eq "nor not") 664 { 665 if ($match and !$out{$postcode}) { $out{$postcode}++; } 666 else { delete $out{$postcode} if $out{$postcode}; } 667 } 668 elsif ($mode eq "xor") 669 { 670 if ($match) 671 { 672 if ($out{$postcode}) { delete $out{$postcode}; } 673 else { $out{$postcode}++; } 674 } 675 } 676 elsif ($mode eq "xor not") 677 { 678 unless ($match) 679 { 680 if ($out{$postcode}) { delete $out{$postcode}; } 681 else { $out{$postcode}++; } 682 } 683 } 684 685 elsif ($mode eq "xnor") 686 { 687 my $boolean = $out{$postcode} ? 1 : 0; 688 if ($match == $boolean) 689 { 690 $out{$postcode}++; 691 } 692 else 693 { 694 delete $out{$postcode} if $out{$postcode}; 695 } 696 } 697 elsif ($mode eq "xnor not") 698 { 699 my $boolean = $out{$postcode} ? 1 : 0; 700 if ($match != $boolean) 701 { 702 $out{$postcode}++; 703 } 704 else 705 { 706 delete $out{$postcode} if $out{$postcode}; 707 } 708 } 709 } 710 } 711 712 ############################################################################### 713 714 return unless %out; 715 # Return nothing if we have an empty list (or rather, hash). 716 717 my @out; 718 719 if ($limit) # The caller has requested just one postcode, # 720 { # and will get exactly that if any matches # 721 my @list = keys %out; # were found. The returned postcode is chosen # 722 @out = $list[rand(@list)]; # by random. # 723 } 724 else 725 { 726 @out = sort keys %out; 727 # This will give an ordered list, as opposed to a semi random order. This # 728 # is essential when comparing lists of postcodes, as the test scripts do. # 729 } 730 731 ############################################################################### 732 733 return @out unless $objects_requested; 734 735 my @out_objects; 736 737 foreach my $postcode (@out) 738 { 739 push(@out_objects, $caller_class->new($postcode)); 740 } 741 742 return @out_objects; 743} 744 745 746sub _proc_pointer 747{ 748 my $procedure_name = shift; 749 return \&{$procedure_name}; 750} 751 752sub _valid_procedure_pointer 753{ 754 my $ptr = shift; 755 return 0 if ref $ptr ne "CODE"; 756 return 1 if defined(&$ptr); 757 return 0; 758} 759 7601; 761__END__ 762 763=head1 NAME 764 765Geo::Postcodes - Base class for the Geo::Postcodes::* modules 766 767=head1 SYNOPSIS 768 769This module should not be used directly from application programs, but from a 770country subclass; e.g.: 771 772 package Geo::Postcodes::U2; 773 774 use Geo::Postcodes 0.30; 775 use base qw(Geo::Postcodes); 776 777 use strict; 778 use warnings; 779 780 our $VERSION = '0.30'; 781 782And so on. See the documentation for making country subclasses for the gory 783details; I<perldoc Geo::Postcodes::Subclass> or I<man Geo::Postcodes::Subclass>. 784 785=head1 ABSTRACT 786 787Geo::Postcodes - Base class for the Geo::Postcodes::* modules. It is 788useless on its own. 789 790=head1 PROCEDURES AND METHODS 791 792These procedures and methods should, with a few exceptions, not be used directly, 793but from a country module. See the documentation for the indiviual country modules 794for usage details. 795 796=head2 address, borough, county, location, owner, postcode, type, type_verbose 797 798Methods for accessing the fields of a postcode object. The individual country 799modules can support as many of them as needed, and add new ones. 800 801=head2 address_of, borough_of, county_of, location_of, owner_of, postcode_of, 802 type_of, type_verbose_of 803 804Procedures that returns the value of the corresponding field for the given postcode. 805They will return I<undef> if the postcode does not exist, or the field is without 806value for the given postcode. 807 808=head2 get_fields, is_field 809 810I<get_fields()> will return a list of all the fields supported by the module, and 811I<is_field($field)> will return true (1) if the specified field is supported by 812the module. 813 814=head2 legal, valid 815 816Procedures that return I<true> if the postcode is legal (syntactically), or valid 817(in actual use). 818 819=head2 new 820 821This will create a new postcode object. 822 823=head2 selection, selection_loop 824 825Procedures/methods for selecting several postcodes at once. 826 827See the selection manual (I<perldoc Geo::Postcodes::Selection> or 828I<man Geo::Postcodes::Selection>) for usage details, and the tutorial 829(I<perldoc Geo::Postcodes::Tutorial> or I<man Geo::Postcodes::Tutorial>) 830for sample code. 831 832=head2 verify_selectionlist, is_legal_selectionmode, is_legal_initial_selectionmode 833 get_selectionmodes, get_initial_selectionmodes 834 835Supporting procedures when using I<selection> or I<selection_loop>. 836 837See the selection manual; I<perldoc Geo::Postcodes::Selection> or 838I<man Geo::Postcodes::Selection> for usage details. 839 840=head2 get_postcodes 841 842This will return an unsorted list of all the postcodes. 843 844=head2 get_types 845 846This will return a list of types. See the next section. 847 848=head2 type2verbose 849 850 my $type_as_english_text = $Geo::Postcodes::type2verbose($type); 851 my $type_as_national_text = $Geo::Postcodes::U2:type2verbose($type); 852 853This procedure gives an english description of the type. Use the child class 854directly for a description in the native language. 855 856=head1 TYPE 857 858This class defines the following types for the postal locations: 859 860=over 861 862=item BX 863 864Post Office box 865 866=item ST 867 868Street address 869 870=item SX 871 872Service box (as a Post Office box, but the mail is delivered to 873the customer). 874 875=item IO 876 877Individual owner (a company with its own postcode). 878 879=item STBX 880 881Either a Street address (ST) or a Post Office box (BX) 882 883=item MU 884 885Multiple usage (a mix of the other types) 886 887=item PP 888 889Porto Paye receiver (mail where the reicever will pay the postage). 890 891=item PN 892 893Place name 894 895=back 896 897The child classes can use them all, or only a subset, but must not define 898their own additions. The child classes are responsible for adding descriptions 899in the native language, if appropriate. 900 901=head1 DESCRIPTION 902 903This is the base class for the Geo::Postcodes::* modules. 904 905=head1 CAVEAT 906 907This module uses I<inside out objects>, see for instance 908L<http://www.stonehenge.com/merlyn/UnixReview/col63.html> for a discussion of 909the concept. 910 911=head1 SEE ALSO 912 913See also the selection manual (I<perldoc Geo::Postcodes::Selection> or 914I<man Geo::Postcodes::Selection>) for usage details, the tutorial 915(I<perldoc Geo::Postcodes::Tutorial> or I<man Geo::Postcodes::Tutorial>) 916for sample code, and the ajax tutorial (I<perldoc Geo::Postcodes::Ajax> or 917I<man Geo::Postcodes::Ajax>) for information on using the modules in 918combination with ajax code in a html form to get the location updated 919automatically. 920 921The latest version of this library should always be available on CPAN, but see 922also the library home page; F<http://bbop.org/perl/GeoPostcodes> for additional 923information and sample usage. The child classes that can be found there have 924some sample programs. 925 926=head1 COPYRIGHT AND LICENCE 927 928Copyright (C) 2006 by Arne Sommer - perl@bbop.org 929 930This library is free software; you can redistribute them and/or modify 931it under the same terms as Perl itself. 932 933=cut 934