package RefDB::Makestyle;

use 5.008006;
use strict;
use warnings;
use base qw(Exporter);

our $VERSION = '1.2';

=head1 NAME

RefDB::Makestyle - methods used by refdb-ms (RefDB style generator)

=head1 SUMMARY

Makestyle.pm - a module used by I<refdb-ms> (RefDB MakeStyle) -- a utility that generates RefDB bibliography I<style>s.

Provides C<< Element >> and C<< Attribute >> classes for use in generating RefDB I<style>s.

=head1 DEPENDENCIES

The following perl modules are required by this library:

=over

=item

B<Scalar::Util>

Provides a method of querying an object to determine its base class.

=item

B<Term::Clui>

Provides user interface.

For input it provides mechanisms for selection from menus, direct input and for yes/no question-answer prompts.

For output it provides for console output or display in the default editor.

This module, by default, records user choices in a hidden database.  This behaviour is turned off.

Term::Clui::edit uses a console editor for display.  It uses the editor specified by the EDITOR variable.  If this variable is not set it falls back to the 'vi' editor.

=item

B<Text::Wrap>

Provides word-wrap for console display.

=item

B<File::Temp>

Provides useful methods for filename generation.

The OO methods are not used because, at the time of writing, the version shipped with some *nix-like OSes does not yet include the OO interface.

=back

=cut

use Scalar::Util 'blessed';
use Term::Clui;
$ENV{'CLUI_DIR'} = "OFF"; 
use Text::Wrap;
use File::Temp;

=head1 UNIVERSAL METHODS

These methods are supplied by a hidden base class.

=cut

package _Base;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self  = {};
		bless ($self , $class);
		return $self;
	}


=over

=item

B<get_classname>

Returns classname of object.

=cut

	sub get_classname   { Scalar::Util::blessed( $_[0] ); }

=item

B<_is_valid_value>

Default method for determining validity of simple (scalar) user input.  Checks that value is defined and not an empty string and not filled with whitespace.

Is overridden by derived classes with more stringent input requirements.

=cut

	sub _is_valid_value {
		if ( defined( $_[1] ) && ( $_[1] ne "" ) && ( not $_[1] =~ /^\s+$/ ) ) {
			1;
		} else {
			print "Invalid value.  Sorry, please try again.\n";
			0;
		}
	}

=item

B<_input_choice>

User selects from a menu.

Returns user's selection.

Parameters: (0 = class), 1 = prompt, 2... = options.

Common usage:

	my $value = undef;
	while ( 1 ) {
		$value = $self->_input_choice( "Select value:" , <@list> );
		if ( $value ) { last; }
		print "Invalid choice.  Sorry, please try again.\n";
	}

=cut

	sub _input_choice {
		my $self = shift;
		Term::Clui::choose( shift , @_ );
	}

=item

B<_input_ask>

User enters a value.

Returns user's input.

Parameters: (0 = class), 1 = prompt, 2 = default.

Common usage:

	my $value = undef;
	while ( 1 ) {
		$value = $self->_input_ask( "Enter value:" , <$default> );
		if ( $self->_is_valid_value( $value ) ) { last; }
	}

=cut

	sub _input_ask { Term::Clui::ask( $_[1] , $_[2] ); }

=item

B<_input_confirm>

User is asked a question to which s/he answers y/n.

Returns boolean.

Parameters: (0 = class), 1 = question.

Note: If multi-line question, then after answer only the first line is left on screen.  The first line should be a short question with subsequent lines holding further information.

Common usage:

	if (_input_confirm( "Short question?\n\nMore\nmulti-line\ntext." ) ) {
		# do stuff
	}

=cut

	sub _input_confirm { Term::Clui::confirm( $_[1] ); }

=item

B<_display>

Displays screen text with word wrapping.

Parameters: (0 = class), 1 = display string.

Common usage:

	_display( <$scalar> );

=cut

	sub _display { print Text::Wrap::wrap( "" , "" , $_[1] . "\n" ); }

=item

B<_view>

Displays large volume of text in default editor and then returns viewer to original screen.

Parameters: (0 = class), 1 = title , 2 = help text.

Common usage:

	_view( <$title> , <$help_text> );

=cut

	sub _view {
		my ( $self , $title , $body ) = ( shift , shift , shift );
		my $text = "\n"
			. $title 
			. "\n\n" 
			. "[This text should be displaying in your default editor.\n"
			. " If no default editor is specified, vi(m) is used.\n" 
			. " To exit this screen, exit the editor as you normally would" 
			. " - 'ZQ' for vi(m).]" 
			. "\n\n" 
			. $body;
		Term::Clui::edit( $title , $text );
	}

=item

B<_entitize>

Takes string and replaces '&', '<' and '>' with xml entity equivalents.

=back

=cut

	sub _entitize {
		my ( $self , $string ) = ( shift , shift );
		if ( $string ) {
			$string =~ s/&amp;/&/g;
			$string =~ s/&/&amp;/g;
			$string =~ s/&lt;/</g;
			$string =~ s/</&lt;/g;
			$string =~ s/&gt;/>/g;
			$string =~ s/>/&gt;/g;
		}
		return $string;
	}

=head1 ATTRIBUTE CLASSES

These classes model the XML attributes found in the Refdb C<citestylex> DTD.

=head2 Attributes: Public Interface

=cut

package _Attribute;
	use base qw/ _Base /;

=head3 Attributes: Data Members

	%self = (

		name          =>  "ATTRIBUTE" ,

		enumeration   =>  [ "value_0" , "value_1" , ... ] ,

		default       =>  "value_x" , 

		prompt        =>  "This is the purpose and/or use of the attribute." ,

		value         =>  "User selected/entered value>" ,

		summary       =>  0 | 1 ,

	)

=cut

	# Constructor
	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);

=over

=item

B<name>

Attribute name as per the Refdb C<citestylex> DTD.  Always uppercase.

=cut

		$self->{name}        = $class->default_name();

=item

B<enumeration>

List of possible values for attribute.  If empty then attribute takes user-entered value, eg. DISPLAYAUTHOR, which takes a number.

=cut

		$self->{enumeration} = $class->default_enumeration();
		
=item

B<default>

The default value.  If C<enumeration> is present, the default will always be one of the items in it.  If no default then leave as C<undef> -- this is the 'default' value after object construction.

=cut

		$self->{default}     = $class->default_default();

=item

B<prompt>

The text string that is used by client methods to prompt users when entering data.

=cut

		$self->{prompt}      = $class->default_prompt();

=item

B<summary>

Boolean determining whether attribute is included in summary form.

=cut

		$self->{summary}     = $class->default_summary();
		
=item

B<value>

The attribute value as selected or input by the user.

=back

=cut

		$self->{value}       = $class->default_value();

=head3 Attributes: Constructors

In following method signatures replace ATTRIBUTE with the relevant attribute class name.

Some attributes have a maximum number of instances that can exist within a given PubType (see method I<select_value> below).  When a class is asked to create an object in excess of the designed maximum for that class the constructor returns a scalar holding an error message.  This enables a simple test (using C<ref> function) to determine whether the constructor returned an object/blessed variable or scalar.

	my $at = ATTRIBUTE->new();

=cut

		return $self;
	}

	# Default values (abstract)
	sub default_name        { undef; }
    sub default_default     { undef; }
    sub default_prompt      { undef; }
    sub default_enumeration { undef; }
	sub default_summary     {     0; }
    sub default_value       { undef; }

=head3 Attributes: Setters

	$at->set_name        ( "<name>"    );
	$at->set_default     ( "<default>" );
	$at->set_prompt      ( "<prompt>"  );
	$at->add_enumeration ( LIST        );
	$at->set_summary     ( 0 | 1       );

=cut

	sub set_name        { $_[0]->{name}     = $_[1]; }
	sub set_default     { $_[0]->{default}  = $_[1]; }
	sub set_prompt      { $_[0]->{prompt}   = $_[1]; }
	sub add_enumeration { push @{ $_[0]->{enumeration} } , @_[1..$#_]; }
	sub set_summary     { $_[0]->{summary}  = $_[1]; }
	sub _set_value      { $_[0]->{value}    = $_[1]; }

=head3 Attributes: Getters

	$at->get_name();
	$at->get_default();
	$at->get_prompt();
	$at->get_enumeration();  # returns list with default value first
	$at->get_enum_string();  # returns single text string
	$at->get_summary();
	$at->get_value();

=cut

	sub get_name        { $_[0]->{name}; }
	sub get_default     { $_[0]->{default}; }
	sub get_prompt      { $_[0]->{prompt}; }
	sub get_value       { $_[0]->{value}; }
	sub get_summary     { $_[0]->{summary}; }
	sub get_enumeration { # puts default value first
		my $self = shift;
		if ( defined( $self->{enumeration} ) ) {
			my $default = $self->get_default();
			my @enum = grep /\b$default\b/ , @{ $self->{enumeration} };
			push( @enum, grep( $_ ne $default , @{ $self->{enumeration} } ) );
			return @enum;
		} else { 
			return undef;
		}
	}
	sub get_enum_string { defined( $_[0]->{enumeration} ) ? "@{ $_[0]->{enumeration} }" : undef; }

=head3 Attributes: Other methods

=over

=item

B<$at-E<gt>select_value();>

If enumeration exists then user selects from list. The default is always the first option in the list.

If there is no enumeration the user enters the value directly. The default value is given and can be selected by simply pressing C<Enter>.

Some attributes are set automatically depending on how many objects of this class have been created.  In those classes this method has no effect.  This applies to the I<Role_AuthorList>, I<Role_PubDate>, I<Role_Title>, I<Role_UserDef>, I<Role_Link> and I<Role_Misc> classes.

The C<citestylex> DTD design envisages a maximum number of objects of these classes being constructed -- the number varying between 3 and 5 depending on the class.  See L</"Attributes: Constructors"> for what happens when the class is asked to create an object in excess of the designed maximum.

=cut

	sub select_value {
		my $self = shift;
		print "Attribute: " . $self->get_name() . "\n";
		$self->_display( $self->get_prompt() );
		my $value = undef;
		if ( defined ( $self->get_enumeration() ) ) {  # choose from enum list
			if ( scalar( $self->get_enumeration() ) == 0 ) { return undef; }
			while ( 1 ) {
				$value = $self->_input_choice( "Select value:" , $self->get_enumeration() );
				if ( defined( $value ) ) { last; }
				print "Invalid choice.  Sorry, please try again.\n";
			}
		} else {  # enter value directly
			while ( 1 ) {
				$value = $self->_input_ask( "Enter value:" , $self->get_default() );
				if ( $self->_is_valid_value( $value ) ) { last; }
			}
			$value = $self->_entitize( $value );
		}
		$self->{value} = $value;
	}

=item

B<$at-E<gt>get_xml_fragment();>

Returns minimal xml fragment indicating the attribute name and value:

	ATTRIBUTE="VALUE"

=back

=cut

	sub get_xml_fragment {
		sprintf "%s=\"%s\"" , 
			$_[0]->get_name() , 
			defined( $_[0]->get_value() ) ? $_[0]->get_value() : "";
	}

=head2 Attributes: List of Classes

To see a list of attribute classes use the C<document-dtd-entities> utility that shipped with this program.  It extracts attribute and element properties from this script, assembles and formats them into a single html document.

=cut

package Type_PubType;
	use base qw/ _Attribute /;

	our %_level = (
		"ABST"		=>  0 , 
		"ADVS"		=>  0 , 
		"ART"		=>  0 , 
		"BILL"		=>  0 , 
		"BOOK"		=>  0 , 
		"CASE"		=>  0 , 
		"CHAP"		=>  0 , 
		"COMP"		=>  0 , 
		"CONF"		=>  0 , 
		"CTLG"		=>  0 , 
		"DATA"		=>  0 , 
		"ELEC"		=>  0 , 
		"GEN"		=>  0 , 
		"ICOMM"		=>  0 , 
		"INPR"		=>  0 , 
		"JFULL"		=>  0 , 
		"JOUR"		=>  0 , 
		"MAP"		=>  0 , 
		"MGZN"		=>  0 , 
		"MPCT"		=>  0 , 
		"MUSIC"		=>  0 , 
		"NEWS"		=>  0 , 
		"PAMP"		=>  0 , 
		"PAT"		=>  0 , 
		"PCOMM"		=>  0 , 
		"RPRT"		=>  0 , 
		"SER"		=>  0 , 
		"SLIDE"		=>  0 , 
		"SOUND"		=>  0 , 
		"STAT"		=>  0 , 
		"THES"		=>  0 , 
		"UNBILL"	=>  0 , 
		"UNPB"		=>  0 , 
		"VIDEO"		=>  0 ,	
	);

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "TYPE" );
		$self->add_enumeration( qw/ ABST ADVS ART BILL BOOK CASE CHAP COMP CONF CTLG DATA ELEC GEN ICOMM INPR JFULL JOUR MAP MGZN MPCT MUSIC NEWS PAMP PAT PCOMM RPRT SER SLIDE SOUND STAT THES UNBILL UNPB VIDEO / );
		$self->set_default    ( "GEN" );
		$self->set_prompt     ( "The type of reference.  Here is a summary (see manual for more detail): GEN (default style -- you really should include this one!), ABST (abstract), ADVS (audiovisual material), ART (art work), BILL (resolution/bill), BOOK (whole book), CASE (case), CHAP (book chapter), COMP (computer program), CONF (conference proceeding), CTLG (catalog), DATA (data file), ELEC (electronic citation), HEAR (hearing), ICOMM (internet communication), INPR (in press), JFULL (entire journal/periodical), JOUR (journal/periodical article), MAP (map), MGZN (magazine), MPCT (motion picture), MUSIC (music score), NEWS (newspaper), PAMP (pamphlet), PAT (patent), PCOMM (personal communication), RPRT (report), SER (serial--book, monograph), SLIDE (slide), SOUND (sound recording), STAT (statute), THES (thesis/dissertation), UNBILL (unenacted bill/resolution), UNPB (unpublished), VIDEO (video recording)");
		$self->set_summary( 1 );
		$self->_assign_level();  # Class-specific
		return defined( $self->get_value() ) ? $self : "Error: Maximum number of " . $self->get_name() . " attributes already exist for element PUBTYPE.";
		return $self;
	}

	# Class-specific

	sub _assign_level { 
		my ( $self , $value ) = ( shift , undef );
		if ( scalar( $self->get_enumeration() ) != 0 ) {
			print "Attribute: " . $self->get_name() . "\n";
			print $self->_display( $self->get_prompt() );
			while ( 1 ) {
				$value = $self->_input_choice( "Select value: " , $self->get_enumeration() );
				if ( defined( $value ) ) { last; }
				print "Invalid choice.  Sorry, please try again.\n";
			}
			$_level{$value} = 1;
			$self->{value} = $value;
		}
	}

	sub DESTROY { if ( $_[0]->get_value() ) { $_level{$_[0]->get_value()} = 0 ; } }

	sub select_value { }

	sub get_enumeration {
		my ( $self , @enum , $key , $value ) = ( shift );
		while ( ( $key , $value ) = each %_level ) {
			if ( not ( $_level{$key} ) ) { push( @enum , $key ); }
		}
		my ( $default , @ordered_array ) = ( $self->get_default() );
		if ( grep /\b$default\b/ , @enum ) {
			@ordered_array = grep /\b$default\b/ , @enum;
			push( @ordered_array, grep( $_ ne $default , @enum ) );
			@enum = @ordered_array;
		}
		return @enum;
	}

package Type_PageRange;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "TYPE" );
		$self->add_enumeration( qw/ ABBREV CHICAGO FULL STARTONLY TWODIGIT / );
		$self->set_default    ( "FULL" );
		$self->set_prompt     ( "Default = \"FULL\".\nDetermines how a page range is displayed.  Assuming a page range of 3336-3339, the various options result in -- ABBREV: '3336-9', CHICAGO: '3336-3339', FULL: '3336-3339', STARTONLY: '3336' and TWODIGIT: '3336-39'." );
		return $self;
	}

package Style;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "STYLE" );
		$self->add_enumeration( qw/ BOLD BOLDITALIC BOLDITULINE BOLDULINE ITALIC ITULINE NONE SUB SUPER ULINE / );
		$self->set_default    ( "NONE" );
		$self->set_prompt     ( "Sets the style of displayed text.  Options -- BOLD = bold, BOLDITALIC = bold italic, BOLDITULINE = bold italic underlined, BOLDULINE = bold underlined, ITALIC = italic, ITULINE = italic underlined, NONE = no special formatting, SUB = subscript, SUPER = superscript and ULINE = underlined." );
		return $self;
	}

package AlternateStyle;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "ALTERNATESTYLE" );
		$self->add_enumeration( qw/ ABOLD ABOLDITALIC ABOLDITULINE ABOLDULINE AITALIC AITULINE ASUB ASUPER AULINE NONE / );
		$self->set_default    ( "NONE" );
		$self->set_prompt     ( "Warning: Not currently implemented.\nSome author-date bibliographic styles use title instead of author when there is no author specified.  This attribute determines the style of the title when it is used this way.  Options -- ABOLD = bold, ABOLDITALIC = bold italic, ABOLDITULINE = bold italic underlined, ABOLDULINE = bold underlined, AITALIC = italic, AITULINE = italic underlined, ASUB = subscript, ASUPER = superscript, AULINE = underlined and ANONE = no special formatting." );
		return $self;
	}

package Role_AuthorList;
	use base qw/ _Attribute /;

	our %_level = (
		"1"  =>  "PRIMARY" ,
		"2"  =>  "SECONDARY" , 
		"3"  =>  "TERTIARY" ,
	);

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "ROLE" );
		$self->add_enumeration( qw/ PRIMARY SECONDARY TERTIARY / );
		$self->set_default    ( "PRIMARY" );
		$self->set_prompt     ( "Determines which 'level' of author the enclosing element refers to." );
		$self->set_summary( 1 );
		++ $_level{"level"};     # Class-specific
		$self->_assign_level();  # Class-specific
		return defined( $self->get_value() ) ? $self : "Error: Maximum number of " . $self->get_name() . " attributes already exist for element AUTHORLIST.";
	}

	# Class-specific
	sub _assign_level { $_[0]->{value} = $_level{ $_level{"level"} }; }

	sub DESTROY { -- $_level{"level"}; }

	sub select_value { }

	sub reset_counter { $_level{"level"} = 0; }

package Role_PubDate;
	use base qw/ _Attribute /;

	our %_level = (
		"1"  =>  "PRIMARY" ,
		"2"  =>  "SECONDARY" , 
	);

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "ROLE" );
		$self->add_enumeration( qw/ PRIMARY SECONDARY / );
		$self->set_default    ( "PRIMARY" );
		$self->set_prompt     ( "Determines which 'level' of publication date the enclosing element refers to." );
		$self->set_summary( 1 );
		++ $_level{"level"};     # Class-specific
		$self->_assign_level();  # Class-specific
		return defined( $self->get_value() ) ? $self : "Error: Maximum number of " . $self->get_name() . " attributes already exist for element PUBDATE.";
	}

	# Class-specific
	sub _assign_level { $_[0]->{value} = $_level{ $_level{"level"} }; }

	sub DESTROY { -- $_level{"level"}; }

	sub select_value { }

	sub reset_counter { $_level{"level"} = 0; }

package Role_Title;
	use base qw/ _Attribute /;

	our %_level = (
		"1"  =>  "PRIMARY" ,
		"2"  =>  "SECONDARY" , 
		"3"  =>  "TERTIARY" ,
	);

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "ROLE" );
		$self->add_enumeration( qw/ PRIMARY SECONDARY TERTIARY / );
		$self->set_default    ( "PRIMARY" );
		$self->set_prompt     ( "Determines which 'level' of title the enclosing element refers to." );
		$self->set_summary( 1 );
		++ $_level{"level"};     # Class-specific
		$self->_assign_level();  # Class-specific
		return defined( $self->get_value() ) ? $self : "Error: Maximum number of " . $self->get_name() . " attributes already exist for element TITLE.";
	}

	# Class-specific
	sub _assign_level { $_[0]->{value} = $_level{ $_level{"level"} }; }

	sub DESTROY { -- $_level{"level"}; }

	sub select_value { }

	sub reset_counter { $_level{"level"} = 0; }

package Role_UserDef;
	use base qw/ _Attribute /;

	our %_level = (
		"1"  =>  0 ,
		"2"  =>  0 , 
		"3"  =>  0 ,
		"4"  =>  0 ,
		"5"  =>  0 ,
	);

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "ROLE" );
		$self->add_enumeration( qw/ 1 2 3 4 5 / );
		$self->set_default    ( "1" );
		$self->set_prompt     ( "Determines which user-defined field (U1 - U5) the enclosing element refers to. Each possible option can be used in only one attribute." );
		$self->set_summary( 1 );
		$self->_assign_level();  # Class-specific
		return defined( $self->get_value() ) ? $self : "Error: Maximum number of " . $self->get_name() . " attributes already exist for element USERDEF.";
	}

	# Class-specific

	sub _assign_level { 
		my ( $self , $value ) = ( shift , undef );
		if ( scalar( $self->get_enumeration() ) != 0 ) {
			print "Attribute: " . $self->get_name() . "\n";
			print $self->_display( $self->get_prompt() );
			while ( 1 ) {
				$value = $self->_input_choice( "Select value: " , sort( $self->get_enumeration() ) );
				if ( defined( $value ) ) { last; }
				print "Invalid choice.  Sorry, please try again.\n";
			}
			$_level{$value} = 1;
			$self->{value} = $value;
		}
	}

	sub DESTROY { if ( $_[0]->get_value() ) { $_level{$_[0]->get_value()} = 0 ; } }

	sub select_value { }

	sub get_enumeration {
		my ( @enum , $key , $value ) = ( () );
		while ( ( $key , $value ) = each %_level ) {
			if ( not ( $_level{$key} ) ) { push( @enum , $key ); }
		}
		return @enum;
	}

	sub reset_counter { foreach ( keys %_level ) { $_level{$_} = 0; } }

package Role_Misc;
	use base qw/ _Attribute /;

	our %_level = (
		"1"  =>  0 ,
		"2"  =>  0 , 
		"3"  =>  0 ,
	);

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "ROLE" );
		$self->add_enumeration( qw/ 1 2 3 / );
		$self->set_default    ( "1" );
		$self->set_prompt     ( "Determines which miscellaneous field (M1 - M5) the enclosing element refers to.  Each possible option can be used in only one attribute." );
		$self->set_summary( 1 );
		$self->_assign_level();
		return defined( $self->get_value() ) ? $self : "Error: Maximum number of " . $self->get_name() . " attributes already exist for element MISC.";
	}

	# Class-specific

	sub _assign_level { 
		my ( $self , $value ) = ( shift , undef );
		if ( scalar( $self->get_enumeration() ) != 0 ) {
			print "Attribute: " . $self->get_name() . "\n";
			print $self->_display( $self->get_prompt() );
			while ( 1 ) {
				$value = $self->_input_choice( "Select value: " , sort( $self->get_enumeration() ) );
				if ( defined( $value ) ) { last; }
				print "Invalid choice.  Sorry, please try again.\n";
			}
			$_level{$value} = 1;
			$self->{value} = $value;
		}
	}

	sub DESTROY { if ( $_[0]->get_value() ) { $_level{$_[0]->get_value()} = 0 ; } }

	sub select_value { }

	sub get_enumeration {
		my ( @enum , $key , $value ) = ( () );
		while ( ( $key , $value ) = each %_level ) {
			if ( not ( $_level{$key} ) ) { push( @enum , $key ); }
		}
		return @enum;
	}

	sub reset_counter { foreach ( keys %_level ) { $_level{$_} = 0; } }

package Role_Link;
	use base qw/ _Attribute /;

	our %_level = (
		"0"  =>  0 ,
		"1"  =>  0 ,
		"2"  =>  0 , 
		"3"  =>  0 ,
		"4"  =>  0 ,
	);

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "ROLE" );
		$self->add_enumeration( qw/ 0 1 2 3 4 / );
		$self->set_default    ( "0" );
		$self->set_prompt     ( "Determines which link field the enclosing element refers to: 1-4 correspond to L1-L4, 0 corresponds to UR.  Each possible option can be used in only one attribute." );
		$self->set_summary( 1 );
		$self->_assign_level();
		return defined( $self->get_value() ) ? $self : "Error: Maximum number of " . $self->get_name() . " attributes already exist for element LINK.";
	}

	# Class-specific

	sub _assign_level { 
		my ( $self , $value ) = ( shift , undef );
		if ( scalar( $self->get_enumeration() ) != 0 ) {
			print "Attribute: " . $self->get_name() . "\n";
			print $self->_display( $self->get_prompt() );
			while ( 1 ) {
				$value = $self->_input_choice( "Select value: " , sort( $self->get_enumeration() ) );
				if ( defined( $value ) ) { last; }
				print "Invalid choice.  Sorry, please try again.\n";
			}
			$_level{$value} = 1;
			$self->{value} = $value;
		}
	}

	sub DESTROY { if ( $_[0]->get_value() ) { $_level{$_[0]->get_value()} = 0 ; } }

	sub select_value { }

	sub get_enumeration {
		my ( @enum , $key , $value ) = ( () );
		while ( ( $key , $value ) = each %_level ) {
			if ( not ( $_level{$key} ) ) { push( @enum , $key ); }
		}
		return @enum;
	}

	sub reset_counter { foreach ( keys %_level ) { $_level{$_} = 0; } }

package DisplayAuthor;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "DISPLAYAUTHOR" );
		# No enumeration
		$self->set_default    ( "255" );
		$self->set_prompt     ( "Used in conjunction with MAXAUTHOR.  If the number of authors exceeds MAXAUTHOR then only the first DISPLAYAUTHOR number of authors are shown.  To show all authors always, select a large number, ie. the default." );
		return $self;
	}

	sub _is_valid_value {
		if ( $_[1] =~ /^\d+$/ ) {  # digits only
			1;
		} else {
			print "Invalid number.  Sorry, please try again.\n";
			0;
		}
	}

package MaxAuthor;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "MAXAUTHOR" );
		# No enumeration
		$self->set_default    ( "256" );
		$self->set_prompt     ( "Used in conjunction with DISPLAYAUTHOR.  If the number of authors exceeds MAXAUTHOR then only the first DISPLAYAUTHOR number of authors are shown.  To show all authors always, select a large number, ie. the default." );
		return $self;
	}

	sub _is_valid_value {
		if ( $_[1] =~ /^\d+$/ ) {  # digits only
			1;
		} else {
			print "Invalid number.  Sorry, please try again.\n";
			0;
		}
	}

package UpperCase;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "UPPERCASE" );
		$self->add_enumeration( qw/ ALL LASTNAME NONE / );
		$self->set_default    ( "NONE" );
		$self->set_prompt     ( "Default = \"NONE\".\nDetermines capitalisation of author names -- ALL: sur- and forenames changed to uppercase, LASTNAME: surnames only altered to uppercase and NONE: no alteration to case of author names." );
		return $self;
	}

package InitialStyle;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "INITIALSTYLE" );
		$self->add_enumeration( qw/ F.M. F.SPCM. FIRSTSPCM. FIRSTSPCMIDDLE FM FSPCM / );
		$self->set_default    ( "F.M." );
		$self->set_prompt     ( "Default = \"F.M.\".\nDetermines the format of author forenames.  The various options will affect the layout of the forenames 'John Henry' thus -- F.M. = 'J.H.', F.SPCM. = 'J. H.', FIRSTSPCM. = 'John H.', FIRSTSPCMIDDLE = 'John Henry', FM = 'JH', FSPCM = 'J H'." );
		return $self;
	}

package NameOrder;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "NAMEORDER" );
		$self->add_enumeration( qw/ FIRSTMIDDLELAST LAST LASTCOMMAFIRSTMIDDLE LASTFIRSTMIDDLE LASTCOMMASPCFIRSTMIDDLE / );
		$self->set_default    ( "LASTFIRSTMIDDLE" );
		$self->set_prompt     ( "Default = \"LASTFIRSTMIDDLE\".\nDetermines how the surname is layed out in relation to the forenames.  This attribute treats the forenames as an atomic whole -- formatting of the forenames is controlled by the attribute 'INITIALSTYLE' (in the following examples INITIALSTYLE is set to 'FM', ie. 'JH').  The various options for NAMEORDER will affect the layout of the name 'Citizen, John Henry' thus -- LASTFIRSTMIDDLE = 'Smith JH', FIRSTMIDDLELAST = 'JH Citizen', LAST = 'Citizen', LASTCOMMAFIRSTMIDDLE = 'Citizen,JH', LASTCOMMASPCFIRSTMIDDLE = 'Citizen, JH'." );
		return $self;
	}

package Sequence;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "SEQUENCE" );
		$self->add_enumeration( qw/ DMY MDY MY Y YMD / );
		$self->set_default    ( "Y" );
		$self->set_prompt     ( "Default = \"Y\".\nDetermines which date elements are displayed, and in what order.  Unsurprisingly, Y = year, M = month and D = day." );
		return $self;
	}

package Format;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "FORMAT" );
		$self->add_enumeration( qw/ NO YES / );
		$self->set_default    ( "YES" );
		$self->set_prompt     ( "Default = \"YES\".\nDetermines whether the associated publication date will be formatted.  If set to 'YES', date format is determined by attributes SEQUENCE, YEARFORMAT, MONTHFORMAT, PADLEADINGZERO and DAYFORMAT.  If set to 'NO', date format defaults to a fourdigit year (eg. '2001') and the aforementioned date formatting attributes are ignored." );
		return $self;
	}

package YearFormat;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "YEARFORMAT" );
		$self->add_enumeration( qw/ FOURDIGIT ROMANYEAR TWODIGIT / );
		$self->set_default    ( "FOURDIGIT" );
		$self->set_prompt     ( "Default = \"FOURDIGIT\".\nDetermines format of year element of publication date.  Has effect only if FORMAT = 'YES'.  Options will format the year '2001' thus: FOURDIGIT = '2001', ROMANYEAR = 'MMI' and TWODIGIT = '01'." );
		return $self;
	}

package MonthFormat;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "MONTHFORMAT" );
		$self->add_enumeration( qw/ ABBREVDOT ARABICMONTH FULL ROMANMONTH THREELETTER / );
		$self->set_default    ( "ARABICMONTH" );
		$self->set_prompt     ( "Default = \"ARABICMONTH\".\nDetermines the format of the month part of the publication date. Has effect only if FORMAT = 'YES'.  The values displayed by the options THREELETTER, ABBREVDOT and FULL are those defined in the corresponding THREELET, ABBREV and FULL attributes, respectively, of the elements JAN, FEB ... NOV, DEC.  The remaining options will format 'October' thus: ARABICMONTH = '10' and ROMANMONTH = 'X'.  Note that ROMANMONTH uses uppercase characters." );
		return $self;
	}

package PadLeadingZero;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "PADLEADINGZERO" );
		$self->add_enumeration( qw/ NN YY / );
		$self->set_default    ( "NN" );
		$self->set_prompt     ( "Default = \"NN\".\nDetermines whether the month and day parts of the publication date are shown with a leading zero when they are < 10." );
		return $self;
	}

package DayFormat;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "DAYFORMAT" );
		$self->add_enumeration( qw/ ARABICDAY ROMANDAY / );
		$self->set_default    ( "ARABICDAY" );
		$self->set_prompt     ( "Default = \"ARABICDAY\".\nDetermines the format of the day part of publication date.  These options will format the day '10' thus: ARABICDAY = '10' and ROMANDAY = 'x'.  Note that ROMANDAY uses lower case characters." );
		return $self;
	}

package Case;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "CASE" );
		$self->add_enumeration( qw/ ASIS ICAPS LOWER UPPER / );
		$self->set_default    ( "ASIS" );
		$self->set_prompt     ( "Default = \"ASIS\".\nDetermines the case of content in the enclosing element.  The various options render 'This text' thus: ASIS = 'This text', ICAPS = 'This Text', LOWER = 'this text' and UPPER = 'THIS TEXT'." );
		return $self;
	}

package AlternateText_AuthorList;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "ALTERNATETEXT" );
		$self->add_enumeration( qw/ AEMPTY NONE TITLEPRIMARY TITLESECONDARY TITLETERTIARY / );
		$self->set_default    ( "AEMPTY" );
		$self->set_prompt     ( "Warning: Not currently implemented.\nDefault = \"AEMPTY\".\nSome author-year styles use title instead of author when the author is not specified.  This attribute determines what value to use instead of author when there is no author.  Options -- AEMPTY = use value specified by element AEMPTY, NONE = author left blank, TITLEPRIMARY = use primary title, TITLESECONDARY = use secondary title and TITLETERTIARY = use tertiary title." );
		return $self;
	}

package AlternateText_JournalName;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "ALTERNATETEXT" );
		$self->add_enumeration( qw/ AABBREV AFULL AUSERONE AUSERTWO / );
		$self->set_default    ( "AFULL" );
		$self->set_prompt     ( "Default = \"AFULL\".\nDetermines, in conjunction with DEFAULTTEXT, which field to use for journal name -- if field specified by DEFAULTTEXT is empty, refdb tries to use field specified by this attribute. The options refer to the following fields: AABBREV = 'JO', AFULL = 'JF', AUSERONE = 'J1', AUSERTWO = 'J2'." );
		return $self;
	}

package DefaultText;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "DEFAULTTEXT" );
		$self->add_enumeration( qw/ ABBREV FULL USERONE USERTWO / );
		$self->set_default    ( "ABBREV" );
		$self->set_prompt     ( "Default = \"ABBREV\".\nIndicates which field refdb should retrieve the journal name from.  Note: if the specified field is empty for a given record, refdb will try to use the field specified by ALTERNATETEXT.  The options refer to the following fields: ABBREV = 'JO', FULL = 'JF', USERONE = 'J1', USERTWO = 'J2'." );
		return $self;
	}

package Punctuation;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "PUNCTUATION" );
		$self->add_enumeration( qw/ PERIOD PERIODSPACE SPACE / );
		$self->set_default    ( "SPACE" );
		$self->set_prompt     ( "Default = \"SPACE\".\nControls formatting of the abbreviated journal name (field 'JO').  The options will format the abbreviated journal name 'J Some.Journal' thus: PERIOD = 'J.Some.Journal', PERIODSPACE = 'J. Some. Journal', SPACE = 'J Some Journal'.  Note: The character immediately following the last part of the journal abbreviation is determined by the FOLLOWING element associated with the JOURNALNAME element." );
		return $self;
	}

package BiblioFirstIndent;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "BIBLIOFIRSTINDENT" );
		# $self->add_enumeration( qw/ BASIS BAUTHORDATE BDATEASC BDATEDESC / );
		# $self->set_default    ( "BAUTHORDAT" );
		$self->set_prompt     ( "Sets indent of the first line of each bibliography entry.  Is cumulative with value set for BIBLIOBLOCKINDENT.  Can be negative.  Units must be included.  Absolute units: 'cm' (centimetres), 'in' (inches), 'mm' (millimetres), 'pc' (picas) or 'pt' (points).  Relative units: '%' (of total width), 'em' (ems, relative to font size) or 'px' (pixels, relative to canvas resolution).  Examples: '5em' and '-3%'." );
		return $self;
	}

	sub _is_valid_value {
		if ( $_[1] =~ /^-?\d+\.?\d*(cm|em|in|mm|pc|px|pt|%)$/ ) {  # decimal number followed by units
			1;
		} else {
			print "Invalid value.  Sorry, please try again.\n";
			0;
		}
	}

package BiblioSequence;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "BIBLIOSEQUENCE" );
		$self->add_enumeration( qw/ BASIS BAUTHORDATE BDATEASC BDATEDESC / );
		$self->set_default    ( "BAUTHORDATE" );
		$self->set_prompt     ( "Default = \"BAUTHORDATE\".\nDetermines the order of references in bibliography.  The options are: BASIS = same order as citations appear in document, BAUTHORDATE = sort on author then date, BDATEASC = sort on date ascending and BDATEDESC = sort on date descending." );
		return $self;
	}

package BiblioBlockIndent;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "BIBLIOBLOCKINDENT" );
		# $self->add_enumeration( qw/ BASIS BAUTHORDATE BDATEASC BDATEDESC / );
		# $self->set_default    ( "BAUTHORDAT" );
		$self->set_prompt     ( "Sets indent of the whole bibliography.  Can be negative.  Units must be included.  Absolute units: 'cm' (centimetres), 'in' (inches), 'mm' (millimetres), 'pc' (picas) or 'pt' (points).  Relative units: '%' (of total width), 'em' (ems, relative to font size) or 'px' (pixels, relative to canvas resolution).  Examples: '5em' and '-3%'." );
		return $self;
	}

	sub _is_valid_value {
		if ( $_[1] =~ /^-?\d+\.?\d*(cm|em|in|mm|pc|px|pt|%)$/ ) {  # decimal number followed by units
			1;
		} else {
			print "Invalid value.  Sorry, please try again.\n";
			0;
		}
	}

package FontSize;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "FONTSIZE" );
		# $self->add_enumeration( qw/ BASIS BAUTHORDATE BDATEASC BDATEDESC / );
		# $self->set_default    ( "BAUTHORDAT" );
		$self->set_prompt     ( "Sets font size of bibliography.  Absolute sizes: 'xx-small', 'x-small', 'small', 'medium', 'large', 'x-large' or 'xx-large'.  Relative sizes: 'smaller' or 'larger'.  Numeric sizes cannot be negative.  Numeric absolute units: 'in' (inches), 'cm' (centimetres), 'mm' (millimetres), 'pt' (points) or 'pc' (picas).  Numeric relative units: 'em' (ems, relative to parent font), 'px' (pixels, relative to canvas resolution) or '%' (relative to parent font).  If a number is used without units it is a relative multiplier of the parent font (acts as if 'em' units specified).  Examples: 'x-small', 'larger', '0.8cm', '5em' and '0.8'." );
		return $self;
	}

	sub _is_valid_value {
		if ( $_[1] =~ /^(((x?x-)?(small|large))|smaller|larger|\d+\.?\d*(cm|em|in|mm|pc|px|pt|%)?)$/ ) {  # see prompt for explanation
			1;
		} else {
			print "Invalid value.  Sorry, please try again.\n";
			0;
		}
	}

package InTextSequence;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "INTEXTSEQUENCE" );
		$self->add_enumeration( qw/ ASIS AUTHORDATE DATEASC DATEDESC / );
		$self->set_default    ( "AUTHORDATE" );
		$self->set_prompt     ( "Default = \"AUTHORDATE\".\nDetermines the order of citations in multiple citations.  The options are: ASIS = same order as citations appear in document, AUTHORDATE = sort on author then date, DATEASC = sort on date ascending and DATEDESC = sort on date descending." );
		return $self;
	}

package Threelet_Jan;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "THREELET" );
		# No enumeration
		$self->set_default    ( "Jan" );
		$self->set_prompt     ( "Three letter abbreviation for the first month." );
		return $self;
	}

	sub _is_valid_value {
		if ( $_[1] =~ /^[a-zA-Z][a-zA-Z][a-zA-Z]$/ ) {  # three letters only
			1;
		} else {
			print "Invalid three letter abbreviation.  Sorry, please try again.\n";
			0;
		}
	}

package Threelet_Feb;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "THREELET" );
		# No enumeration
		$self->set_default    ( "Feb" );
		$self->set_prompt     ( "Three letter abbreviation for the second month." );
		return $self;
	}

	sub _is_valid_value {
		if ( $_[1] =~ /^[a-zA-Z][a-zA-Z][a-zA-Z]$/ ) {  # three letters only
			1;
		} else {
			print "Invalid three letter abbreviation.  Sorry, please try again.\n";
			0;
		}
	}

package Threelet_Mar;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "THREELET" );
		# No enumeration
		$self->set_default    ( "Mar" );
		$self->set_prompt     ( "Three letter abbreviation for the third month." );
		return $self;
	}

	sub _is_valid_value {
		if ( $_[1] =~ /^[a-zA-Z][a-zA-Z][a-zA-Z]$/ ) {  # three letters only
			1;
		} else {
			print "Invalid three letter abbreviation.  Sorry, please try again.\n";
			0;
		}
	}

package Threelet_Apr;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "THREELET" );
		# No enumeration
		$self->set_default    ( "Apr" );
		$self->set_prompt     ( "Three letter abbreviation for the fourth month." );
		return $self;
	}

	sub _is_valid_value {
		if ( $_[1] =~ /^[a-zA-Z][a-zA-Z][a-zA-Z]$/ ) {  # three letters only
			1;
		} else {
			print "Invalid three letter abbreviation.  Sorry, please try again.\n";
			0;
		}
	}

package Threelet_May;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "THREELET" );
		# No enumeration
		$self->set_default    ( "May" );
		$self->set_prompt     ( "Three letter abbreviation for the fifth month." );
		return $self;
	}

	sub _is_valid_value {
		if ( $_[1] =~ /^[a-zA-Z][a-zA-Z][a-zA-Z]$/ ) {  # three letters only
			1;
		} else {
			print "Invalid three letter abbreviation.  Sorry, please try again.\n";
			0;
		}
	}

package Threelet_Jun;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "THREELET" );
		# No enumeration
		$self->set_default    ( "Jun" );
		$self->set_prompt     ( "Three letter abbreviation for the sixth month." );
		return $self;
	}

	sub _is_valid_value {
		if ( $_[1] =~ /^[a-zA-Z][a-zA-Z][a-zA-Z]$/ ) {  # three letters only
			1;
		} else {
			print "Invalid three letter abbreviation.  Sorry, please try again.\n";
			0;
		}
	}

package Threelet_Jul;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "THREELET" );
		# No enumeration
		$self->set_default    ( "Jul" );
		$self->set_prompt     ( "Three letter abbreviation for the seventh month." );
		return $self;
	}

	sub _is_valid_value {
		if ( $_[1] =~ /^[a-zA-Z][a-zA-Z][a-zA-Z]$/ ) {  # three letters only
			1;
		} else {
			print "Invalid three letter abbreviation.  Sorry, please try again.\n";
			0;
		}
	}

package Threelet_Aug;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "THREELET" );
		# No enumeration
		$self->set_default    ( "Aug" );
		$self->set_prompt     ( "Three letter abbreviation for the eighth month." );
		return $self;
	}

	sub _is_valid_value {
		if ( $_[1] =~ /^[a-zA-Z][a-zA-Z][a-zA-Z]$/ ) {  # three letters only
			1;
		} else {
			print "Invalid three letter abbreviation.  Sorry, please try again.\n";
			0;
		}
	}

package Threelet_Sep;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "THREELET" );
		# No enumeration
		$self->set_default    ( "Sep" );
		$self->set_prompt     ( "Three letter abbreviation for the ninth month." );
		return $self;
	}

	sub _is_valid_value {
		if ( $_[1] =~ /^[a-zA-Z][a-zA-Z][a-zA-Z]$/ ) {  # three letters only
			1;
		} else {
			print "Invalid three letter abbreviation.  Sorry, please try again.\n";
			0;
		}
	}

package Threelet_Oct;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "THREELET" );
		# No enumeration
		$self->set_default    ( "Oct" );
		$self->set_prompt     ( "Three letter abbreviation for the tenth month." );
		return $self;
	}

	sub _is_valid_value {
		if ( $_[1] =~ /^[a-zA-Z][a-zA-Z][a-zA-Z]$/ ) {  # three letters only
			1;
		} else {
			print "Invalid three letter abbreviation.  Sorry, please try again.\n";
			0;
		}
	}

package Threelet_Nov;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "THREELET" );
		# No enumeration
		$self->set_default    ( "Nov" );
		$self->set_prompt     ( "Three letter abbreviation for the eleventh month." );
		return $self;
	}

	sub _is_valid_value {
		if ( $_[1] =~ /^[a-zA-Z][a-zA-Z][a-zA-Z]$/ ) {  # three letters only
			1;
		} else {
			print "Invalid three letter abbreviation.  Sorry, please try again.\n";
			0;
		}
	}

package Threelet_Dec;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "THREELET" );
		# No enumeration
		$self->set_default    ( "Dec" );
		$self->set_prompt     ( "Three letter abbreviation for the twelfth month." );
		return $self;
	}

	sub _is_valid_value {
		if ( $_[1] =~ /^[a-zA-Z][a-zA-Z][a-zA-Z]$/ ) {  # three letters only
			1;
		} else {
			print "Invalid three letter abbreviation.  Sorry, please try again.\n";
			0;
		}
	}

package Abbrev_Jan;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "ABBREV" );
		# No enumeration
		$self->set_default    ( "Jan." );
		$self->set_prompt     ( "Abbreviation for the first month." );
		return $self;
	}

	sub _is_valid_value {
		if ( $_[1] =~ /^\w+\.?$/ ) {  # only letters, numbers and underscores
			1;
		} else {
			print "Invalid abbreviation.  Sorry, please try again.\n";
			0;
		}
	}

package Abbrev_Feb;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "ABBREV" );
		# No enumeration
		$self->set_default    ( "Feb." );
		$self->set_prompt     ( "Abbreviation for the second month." );
		return $self;
	}

	sub _is_valid_value {
		if ( $_[1] =~ /^\w+\.?$/ ) {  # only letters, numbers and underscores
			1;
		} else {
			print "Invalid abbreviation.  Sorry, please try again.\n";
			0;
		}
	}

package Abbrev_Mar;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "ABBREV" );
		# No enumeration
		$self->set_default    ( "Mar." );
		$self->set_prompt     ( "Abbreviation for the third month." );
		return $self;
	}

	sub _is_valid_value {
		if ( $_[1] =~ /^\w+\.?$/ ) {  # only letters, numbers and underscores
			1;
		} else {
			print "Invalid abbreviation.  Sorry, please try again.\n";
			0;
		}
	}

package Abbrev_Apr;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "ABBREV" );
		# No enumeration
		$self->set_default    ( "Apr." );
		$self->set_prompt     ( "Abbreviation for the fourth month." );
		return $self;
	}

	sub _is_valid_value {
		if ( $_[1] =~ /^\w+\.?$/ ) {  # only letters, numbers and underscores
			1;
		} else {
			print "Invalid abbreviation.  Sorry, please try again.\n";
			0;
		}
	}

package Abbrev_May;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "ABBREV" );
		# No enumeration
		$self->set_default    ( "May" );
		$self->set_prompt     ( "Abbreviation for the fifth month." );
		return $self;
	}

	sub _is_valid_value {
		if ( $_[1] =~ /^\w+\.?$/ ) {  # only letters, numbers and underscores
			1;
		} else {
			print "Invalid abbreviation.  Sorry, please try again.\n";
			0;
		}
	}

package Abbrev_Jun;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "ABBREV" );
		# No enumeration
		$self->set_default    ( "Jun." );
		$self->set_prompt     ( "Abbreviation for the sixth month." );
		return $self;
	}

	sub _is_valid_value {
		if ( $_[1] =~ /^\w+\.?$/ ) {  # only letters, numbers and underscores
			1;
		} else {
			print "Invalid abbreviation.  Sorry, please try again.\n";
			0;
		}
	}

package Abbrev_Jul;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "ABBREV" );
		# No enumeration
		$self->set_default    ( "Jul." );
		$self->set_prompt     ( "Abbreviation for the seventh month." );
		return $self;
	}

	sub _is_valid_value {
		if ( $_[1] =~ /^\w+\.?$/ ) {  # only letters, numbers and underscores
			1;
		} else {
			print "Invalid abbreviation.  Sorry, please try again.\n";
			0;
		}
	}

package Abbrev_Aug;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "ABBREV" );
		# No enumeration
		$self->set_default    ( "Aug." );
		$self->set_prompt     ( "Abbreviation for the eighth month." );
		return $self;
	}

	sub _is_valid_value {
		if ( $_[1] =~ /^\w+\.?$/ ) {  # only letters, numbers and underscores
			1;
		} else {
			print "Invalid abbreviation.  Sorry, please try again.\n";
			0;
		}
	}

package Abbrev_Sep;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "ABBREV" );
		# No enumeration
		$self->set_default    ( "Sep." );
		$self->set_prompt     ( "Abbreviation for the ninth month." );
		return $self;
	}

	sub _is_valid_value {
		if ( $_[1] =~ /^\w+\.?$/ ) {  # only letters, numbers and underscores
			1;
		} else {
			print "Invalid abbreviation.  Sorry, please try again.\n";
			0;
		}
	}

package Abbrev_Oct;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "ABBREV" );
		# No enumeration
		$self->set_default    ( "Oct." );
		$self->set_prompt     ( "Abbreviation for the tenth month." );
		return $self;
	}

	sub _is_valid_value {
		if ( $_[1] =~ /^\w+\.?$/ ) {  # only letters, numbers and underscores
			1;
		} else {
			print "Invalid abbreviation.  Sorry, please try again.\n";
			0;
		}
	}

package Abbrev_Nov;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "ABBREV" );
		# No enumeration
		$self->set_default    ( "Nov." );
		$self->set_prompt     ( "Abbreviation for the eleventh month." );
		return $self;
	}

	sub _is_valid_value {
		if ( $_[1] =~ /^\w+\.?$/ ) {  # only letters, numbers and underscores
			1;
		} else {
			print "Invalid abbreviation.  Sorry, please try again.\n";
			0;
		}
	}

package Abbrev_Dec;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "ABBREV" );
		# No enumeration
		$self->set_default    ( "Dec." );
		$self->set_prompt     ( "Abbreviation for the twelfth month." );
		return $self;
	}

	sub _is_valid_value {
		if ( $_[1] =~ /^\w+\.?$/ ) {  # only letters, numbers and underscores
			1;
		} else {
			print "Invalid abbreviation.  Sorry, please try again.\n";
			0;
		}
	}

package Full_Jan;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "FULL" );
		# No enumeration
		$self->set_default    ( "January" );
		$self->set_prompt     ( "Full spelling of the first month." );
		return $self;
	}

	sub _is_valid_value {
		if ( $_[1] =~ /^\w+$/ ) {  # only letters, numbers and underscores
			1;
		} else {
			print "Invalid abbreviation.  Sorry, please try again.\n";
			0;
		}
	}

package Full_Feb;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "FULL" );
		# No enumeration
		$self->set_default    ( "February" );
		$self->set_prompt     ( "Full spelling of the second month." );
		return $self;
	}

	sub _is_valid_value {
		if ( $_[1] =~ /^\w+$/ ) {  # only letters, numbers and underscores
			1;
		} else {
			print "Invalid abbreviation.  Sorry, please try again.\n";
			0;
		}
	}

package Full_Mar;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "FULL" );
		# No enumeration
		$self->set_default    ( "March" );
		$self->set_prompt     ( "Full spelling of the third month." );
		return $self;
	}

	sub _is_valid_value {
		if ( $_[1] =~ /^\w+$/ ) {  # only letters, numbers and underscores
			1;
		} else {
			print "Invalid abbreviation.  Sorry, please try again.\n";
			0;
		}
	}

package Full_Apr;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "FULL" );
		# No enumeration
		$self->set_default    ( "April" );
		$self->set_prompt     ( "Full spelling of the fourth month." );
		return $self;
	}

	sub _is_valid_value {
		if ( $_[1] =~ /^\w+$/ ) {  # only letters, numbers and underscores
			1;
		} else {
			print "Invalid abbreviation.  Sorry, please try again.\n";
			0;
		}
	}

package Full_May;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "FULL" );
		# No enumeration
		$self->set_default    ( "May" );
		$self->set_prompt     ( "Full spelling of the fifth month." );
		return $self;
	}

	sub _is_valid_value {
		if ( $_[1] =~ /^\w+$/ ) {  # only letters, numbers and underscores
			1;
		} else {
			print "Invalid abbreviation.  Sorry, please try again.\n";
			0;
		}
	}

package Full_Jun;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "FULL" );
		# No enumeration
		$self->set_default    ( "June" );
		$self->set_prompt     ( "Full spelling of the sixth month." );
		return $self;
	}

	sub _is_valid_value {
		if ( $_[1] =~ /^\w+$/ ) {  # only letters, numbers and underscores
			1;
		} else {
			print "Invalid abbreviation.  Sorry, please try again.\n";
			0;
		}
	}

package Full_Jul;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "FULL" );
		# No enumeration
		$self->set_default    ( "July" );
		$self->set_prompt     ( "Full spelling of the seventh month." );
		return $self;
	}

	sub _is_valid_value {
		if ( $_[1] =~ /^\w+$/ ) {  # only letters, numbers and underscores
			1;
		} else {
			print "Invalid abbreviation.  Sorry, please try again.\n";
			0;
		}
	}

package Full_Aug;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "FULL" );
		# No enumeration
		$self->set_default    ( "August" );
		$self->set_prompt     ( "Full spelling of the eighth month." );
		return $self;
	}

	sub _is_valid_value {
		if ( $_[1] =~ /^\w+$/ ) {  # only letters, numbers and underscores
			1;
		} else {
			print "Invalid abbreviation.  Sorry, please try again.\n";
			0;
		}
	}

package Full_Sep;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "FULL" );
		# No enumeration
		$self->set_default    ( "September" );
		$self->set_prompt     ( "Full spelling of the ninth month." );
		return $self;
	}

	sub _is_valid_value {
		if ( $_[1] =~ /^\w+$/ ) {  # only letters, numbers and underscores
			1;
		} else {
			print "Invalid abbreviation.  Sorry, please try again.\n";
			0;
		}
	}

package Full_Oct;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "FULL" );
		# No enumeration
		$self->set_default    ( "October" );
		$self->set_prompt     ( "Full spelling of the tenth month." );
		return $self;
	}

	sub _is_valid_value {
		if ( $_[1] =~ /^\w+$/ ) {  # only letters, numbers and underscores
			1;
		} else {
			print "Invalid abbreviation.  Sorry, please try again.\n";
			0;
		}
	}

package Full_Nov;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "FULL" );
		# No enumeration
		$self->set_default    ( "November" );
		$self->set_prompt     ( "Full spelling of the eleventh month." );
		return $self;
	}

	sub _is_valid_value {
		if ( $_[1] =~ /^\w+$/ ) {  # only letters, numbers and underscores
			1;
		} else {
			print "Invalid abbreviation.  Sorry, please try again.\n";
			0;
		}
	}

package Full_Dec;
	use base qw/ _Attribute /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name       ( "FULL" );
		# No enumeration
		$self->set_default    ( "December" );
		$self->set_prompt     ( "Full spelling of the name of the twelfth month." );
		return $self;
	}

	sub _is_valid_value {
		if ( $_[1] =~ /^\w+$/ ) {  # only letters, numbers and underscores
			1;
		} else {
			print "Invalid abbreviation.  Sorry, please try again.\n";
			0;
		}
	}

=cut

=head1 ELEMENT CLASSES

These classes model the XML elements found in the Refdb C<citestylex> DTD.

=cut

package _Element;
	use base qw/ _Base /;

=head2 Elements: Public Interface

=head3 Elements: Data members

	%self = (

		name          =>  "ELEMENT" ,

		prompt        =>  "This is the purpose of this element." ,

		help          =>  "This is help on how to use this element." ,

		display       =>  undef | "name" | "content" ,

		summary       =>  undef | "name" | "left" | "right" | "content" ,
		
		attributes    =>  {

			mandatory     =>  [ "Attribute_Class_0" , "Attribute_Class_1" , ... ] ,

			optional      =>  [ "Attribute_Class_0" , "Attribute_Class_1" , ... ] ,

			selected      =>  [ "Attribute_0" , "Attribute_1" , ... ] ,

			man_complete  =>  undef | 1 ,

			opt_complete  =>  undef | 1 ,

		} ,

		copyable      =>  1 | undef , 
		
		content       =>  {

			allowed       =>  undef | 1 ,

			suggestion    =>  "Possible value" ,

			value         =>  "User input" ,

			complete      =>  undef | 1 ,
		
		} ,

		model_order   =>  [
			
			[ Element_Class_0 , "? | + | 1" ] ,

			[ Element_Class_1 , "? | + | 1" ] ,

			... ,

		] ,

		model_noorder =>  {

			element_list =>  [ Element_Class_0 , Element_Class_1 , ... ] ,

			separator    =>  "Separator" ,

		} ,

		children      =>  {

			elements     =>  [ Element_0 , Element_1 , ... ] ,

			complete     =>  boolean ,

	)

=over

=cut

	# Constructor
	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self  = {};

=item

B<name>

Element name as given in the Refdb C<citestylex> DTD.  Unique.

=cut

		$self->{name}       = $class->default_name();

=item

B<prompt>

Brief statement of the element's purpose and/or function.

=cut

		$self->{prompt}     = $class->default_prompt();

=item

B<help>

More detailed instructions on how to use this element.

=cut

		$self->{help}       = $class->default_help();

=item

B<display>

Determines whether, and how, to display the element in brief form (used for the "progress report".  If defined, the element is displayed.  If set to "name" the element name is displayed.  If set to "content" the element contents are displayed.

=cut

		$self->{display}    = $class->default_display();

=item

B<summary>

Determines whether, and how, to display the element in summary form.  If defined, the element is displayed.  If set to "name" the element name is displayed.  If set to "content" the element contents are displayed.

=cut

		$self->{summary}    = $class->default_summary();

=item

B<attributes>

Attributes of this element.  First two lists are of 'legal' attributes: mandatory and optional.  Third list is of added attribute objects -- initially empty.  Fourth and fifth hash values are flags indicating whether attributes have been added.

=cut

		$self->{attributes} = $class->default_attributes();

=item

B<content>

Some elements hold #PCDATA content.  The hash key C<allowed> determines whether element accepts input -- I<false>/I<undef> means it does not, I<true> means it does.  The hash key C<suggested> holds a suggested value the user may use.  The hash key C<value> holds the actual user input.  The hash key C<complete> is a flag indicating whether content has been entered.

=cut

		$self->{content}    = $class->default_content();
		
=item

B<copyable>

Some elements cannot be copied, either because they have mandatory attributes which require user input or because they only occur once per citstyle.

=cut

		$self->{copyable}    = $class->default_copyable();
		
=item

B<model_order>

With I<model_noorder> this data member determines the content model for children elements.  All of the 64 elements fall into one of three content models regarding children elements: 1. No children elements, 2. An ordered list and 3. An unordered list.

This data member is C<undef> in cases (1) and (3).

It holds the content model for the thirty elements which have content model (2) -- an ordered list:

	( e[?|+|1] , e[?|+|1] , ... )

where 'e' is the element and the symbols indicate 0-1, 1 or more, and exactly 1 occurrence, respectively.

This data member holds an array of arrays.  Each secondary array has two items: the element class and the number of objects that can be children.

=cut

		$self->{model_order}      = $class->default_order();
=item

B<model_noorder>

With I<model_order> this data member determines the content model for children elements.  All of the 64 elements fall into one of three content models regarding children elements: 1. No children elements, 2. An ordered list and 3. An unordered list.

This data member is C<undef> in cases (1) and (2).

It holds the content model for the four elements (AUTHORONLY, INTEXTDEF, PUBTYPE and YEARONLY) which have content model (3) -- an unordered list:

	( ( e | e | ... ) , e? )+

where 'e' is an element, '?' is 0 or 1 occurrence and '+' is 1 or more occurrances.

This data member holds a hash where the first key points to an array holding the list of alterate elements in the content model inner brackets.  The second key points to the element represented in the content model as 'e+' (in every case it is SEPARATOR).

=cut

		$self->{model_noorder}    = $class->default_noorder();
=item

B<children>

This data member holds a hash.  The first hash key ("elements") points to an array holding the children elements added by the user.  The second hash key ("complete") is a boolean indicating whether all children have been added.

=back

=cut

		$self->{children}   = $class->default_children();

=head3 Elements: Constructors

In following method signatures replace ELEMENT with the relevant element class name.

	my $el = ELEMENT->new();

=cut

		bless ($self , $class);
		return $self;
	}

	# Default values (abstract)
	sub default_name        { undef; }
	sub default_prompt      { undef; }
	sub default_help        { undef; }
	sub default_display     { undef; }
	sub default_summary     { undef; }
	sub default_attributes  { undef; }
	sub default_content     { undef; }
	sub default_copyable    {     1; }
	sub default_order       { undef; }
	sub default_noorder     { undef; }
	sub default_children    { undef; }

=head3 Elements: Setters

	$el->set_name( "<name>" );
	$el->set_prompt( "<prompt>" );
	$el->set_help( "<help>" );
	$el->set_display( undef | "name" | "content" );
	$el->set_summary( undef | "name" | "left" | "right" | "content" );
	$el->set_attributes( %hash );
	$el->set_mandatory_attributes_complete( "1" | undef );
	$el->set_optional_attributes_complete( "1" | undef );
	$el->set_content( %hash );
	$el->set_content_complete( "1" | undef );
	$el->set_order( @ArrayOfArrays );
	$el->set_noorder( %hash );
	$el->set_complete( "1" | "0" | undef );
	$el->_set_value( "<value>" );

=cut

	sub set_name { $_[0]->{name} = $_[1]; }
	sub set_prompt { $_[0]->{prompt} = $_[1]; }
	sub set_help { $_[0]->{help} = $_[1]; }
	sub set_display { $_[0]->{display} = $_[1]; }
	sub set_summary { $_[0]->{summary} = $_[1]; }
	sub set_attributes { $_[0]->{attributes} = $_[1]; }
	sub set_mandatory_attributes_complete { 
		$_[0]->{attributes}->{man_complete} = $_[1];
	}
	sub set_optional_attributes_complete {
		$_[0]->{attributes}->{opt_complete} = $_[1]; 
	}
	sub set_content { $_[0]->{content} = $_[1]; }
	sub set_content_complete { $_[0]->{content}->{complete} = $_[1]; }
	sub set_order { $_[0]->{model_order} = $_[1]; }
	sub set_noorder { $_[0]->{model_noorder} = $_[1]; }
	sub set_complete { $_[0]->{children}->{complete} = $_[1]; }
	sub _set_value { $_[0]->{content}->{value} = $_[1]; }

=head3 Elements: Getters

	$el->get_name();
	$el->get_prompt();
	$el->get_help();
	$el->get_display();
	$el->get_summary();
	$el->get_attr_mandatory();  # list:   $self->{attributes}->{mandatory}
	$el->get_attr_optional();   # list:   $self->{attributes}->{optional}
	$el->get_attr_selected();   # list:   $self->{attributes}->{selected}
	$el->get_suggestion();      # scalar: $self->{content}->{suggestion}
	$el->get_value();           # scalar: $self->{content}->{value}
	$el->get_order();           # AoA:    $self->{model_order}
	$el->get_noorder_list();    # list:   $self->{model_noorder}->{element_list}
	$el->get_noorder_separator; # scalar: $self->{model_noorder}->{separator}
	$el->get_children();        # list:   $self->{children}->{element}
	$el->get_last_child();      # object: $self->{children}->{element}[<last>]

=cut

	sub get_name { $_[0]->{name}; }
    sub get_prompt { $_[0]->{prompt}; }
    sub get_help { $_[0]->{help}; }
    sub get_display { $_[0]->{display}; }
    sub get_summary { $_[0]->{summary}; }
    sub get_attr_mandatory {
		defined( $_[0]->{attributes}->{mandatory} )
			? @{ $_[0]->{attributes}->{mandatory} }
			: undef;
	}
    sub get_attr_optional {
		defined( $_[0]->{attributes}->{optional} ) 
			? @{ $_[0]->{attributes}->{optional} } 
			: undef;
	}
    sub get_attr_selected { 
		defined( $_[0]->{attributes}->{selected} )
			? @{ $_[0]->{attributes}->{selected} }
			: undef;
	}
    sub get_suggestion { $_[0]->{content}->{suggestion}; }
    sub get_value { $_[0]->{content}->{value}; }
    sub get_order {
		defined( $_[0]->{model_order} )
			? @{ $_[0]->{model_order} }
			: undef;
	} 
    sub get_noorder_list {
		defined( $_[0]->{model_noorder}->{element_list} )
			? @{ $_[0]->{model_noorder}->{element_list} }
			: undef;
	} 
    sub get_noorder_separator { $_[0]->{model_noorder}->{separator}; }
    sub get_children {
		defined( $_[0]->{children}->{elements} )
			? @{ $_[0]->{children}->{elements} }
			: undef;
	}
	sub get_last_child { 
		my $self = shift;
		return $self->{children}->{elements}[$self->max_child_index()];
	}

=head3 Elements: Other methods

=over

=item

B<$el-E<gt>content_is_complete();>

Returns boolean indicating whether element content has been entered.

Indicated by a data member which is set after C<enter_value()> method called.

=cut

sub content_is_complete { return $_[0]->{content}->{complete}; }

=item

B<$el-E<gt>enter_value();>

User enters element value if element can legally hold one.  User may be presented with a suggested value.

If the element cannot accept a value nothing happens -- no error message/value is generated.

=cut

	sub enter_value {
		my $self = shift;
		return undef if not $self->is_content_allowed() or $self->content_is_complete();		
		$self->_display( "\nElement: " . $self->get_full_tag() . "\n" );
		$self->_display( "\nEntering element value/content." );
		print "\n";
		$self->_display( $self->get_prompt() );
		my $value = undef;
		while ( 1 ) {
			$value = $self->_input_ask( "Enter value:" , $self->get_suggestion() );
			if ( $self->_is_valid_value( $value ) ) { last; }
		}
		$value = $self->_entitize( $value );
		$self->_set_value( $value );
		$self->set_content_complete( 1 );
	}

=item

B<$el-E<gt>is_content_allowed();>

Returns boolean indicating whether the element can hold content.

=cut

    sub is_content_allowed { $_[0]->{content}->{allowed}; }

=item

B<$el-E<gt>children_are_complete();>

Returns boolean indicating whether all children have been added.

Simply returns $self-E<gt>{children}-E<gt>{complete} and relies on other subroutines having set it appropriately.

=cut

    sub children_are_complete { return $_[0]->{children}->{complete}; }

=item

B<$el-E<gt>max_attribute_index();>

Returns maximum index value of selected attributes list: -1 if empty list.

=cut

	sub max_attribute_index { return $#{ $_[0]->{attributes}->{selected} }; }

=item

B<$el-E<gt>max_child_index();>

Returns maximum index value of element children list: -1 if empty list.

=cut

	sub max_child_index { return $#{ $_[0]->{children}->{elements} }; }

=item

B<$el-E<gt>is_last_element();>

Determines whether element index is the last element in the list of children in the ordered child model: $self-E<gt>{model_order}.

Parameters: [ 0 = class ] , 1 = array index.

=cut

	sub is_last_element { return ( $#{ $_[0]->{model_order} } == $_[1] ); }

=item

B<$el-E<gt>is_last_child();>

Determines whether child index is the last element in the list of children: $self-E<gt>{children}-E<gt>{elements}.

Parameters: [ 0 = class ] , 1 = array index.

=cut

	sub is_last_child { return ( $#{ $_[0]->{children}->{elements} } == $_[1] ); }

=item

B<$el-E<gt>is_creatable();>

Returns a boolean indicating whether this element class can legally create another object.

Intended to be called as a class method.

=cut

	sub is_creatable {
		my ( $proto , $result ) = ( shift , 0 );
		my $class = ref($proto) || $proto;
		if ( ref( $proto ) ) {
			print "Warning: This method ('is_creatable') may return an inaccurate result when called by an object (instance) rather than a class.\n";
		}
		{
			my $element = $class->new();
			if ( ref( $element ) ) {
				if ( $element->_test_attributes_mandatory() ) { 
					$result = 1;
				}
			}
		}
		return $result;
	}

=item

B<$el-E<gt>list_selectable_attributes();>

Returns a list of all optional attributes available for selection.  List consists of all attributes listed in 'optional' list I<minus> all attributes already selected.

=cut

	sub list_selectable_attributes {
		my $self = shift;
		my ( @attributes , @selected );
		if ( $self->max_attribute_index() < 0 ) {
			@attributes = $self->get_attr_optional();
		} else {
			my @optional = $self->get_attr_optional();
			for ( 0 .. $self->max_attribute_index() ) {
				push( @selected , $self->{attributes}->{selected}[$_]->get_classname() );
			}
			foreach my $opt ( @optional ) {
				my @matches = grep $_ eq $opt , @selected;
				if ( not @matches ) { push( @attributes , $opt ); }
			}
		}
		return @attributes;
	}

=item

B<$el-E<gt>_build_attribute_help();>

Helper method for method C<add_attributes_optional>.  Takes as parameter a list of attributes for which help screen is being built.  Includes C<name> and C<prompt> for each attribute.

=cut

	sub _build_attribute_help {
		my ( $self , @attributes , $help ) = ( shift , @_ );
		foreach my $attribute ( @attributes ) { 
			$help .= $attribute->new()->get_name() . "\n";
			$help .= $attribute->new()->get_prompt() . "\n\n";
		}
		return $help;
	}

=item

B<$el-E<gt>has_mandatory_attributes();>

Returns boolean indicating whether element has mandatory attributes.

=cut

	sub has_mandatory_attributes { return $_[0]->{attributes}->{mandatory}; }

=item

B<$el-E<gt>_is_closed_tag();>

Returns boolean indicating whether element can be represented by closed tag.  Requires no possible child elements and no current content.

=cut

	sub _is_closed_tag {
		my ( $self , $closed ) = ( shift , 1 );
		if ( not $self->is_childless_model() ) { $closed = 0; }
		if ( $self->is_content_allowed() && defined( $self->get_value() ) ) { $closed = 0; }
		return $closed;
	}

=item

B<$el-E<gt>get_xml_brief_tag();>

Returns opening xml tag for element.

Shows only mandatory attributes.

=cut

	sub get_xml_brief_tag {
		my ( $self ) = ( shift );
		my $tag = "<" . $self->get_name();
		if ( $self->max_attribute_index() >= 0 && $self->has_mandatory_attributes() ) {
			for ( 0 .. $self->max_attribute_index() ) {
				my $att_name = $self->{attributes}->{selected}[$_]->get_classname();
				if ( grep /$att_name/ , $self->get_attr_mandatory() ) {
					$tag .= " " . $self->{attributes}->{selected}[$_]->get_xml_fragment();
				}
			}
		}
		$tag .= ">";
		return $tag;
	}

=item

B<$el-E<gt>get_xml_open_tag();>

Returns opening xml tag for element.

Parameters: [ 0 = class ] , 1 = show attributes (bool) , 2 = show content (bool).

Designed to be used in conjunction with C<get_xml_close_tag> method.  If element has no potential child elements and no content, then will return closed tag, eg. E<lt>ELEMENT/E<gt>.

=cut

	sub get_xml_open_tag {
		my ( $self , $show_attributes , $show_contents ) = ( shift , shift , shift );
		my $tag = "<" . $self->get_name();
		if ( $show_attributes ) {
			if ( $self->max_attribute_index() >= 0 ) {
				for ( 0 .. $self->max_attribute_index() ) {
					$tag .= " " . $self->{attributes}->{selected}[$_]->get_xml_fragment();
				}
			}
		}
		if ( $self->_is_closed_tag() ) {
			$tag .= "/>";
		} else {
			$tag .= ">";
			if ( $show_contents && $self->get_value() ) {
				$tag .= $self->get_value();
			}
		}
		return $tag;
	}

=item

B<$el-E<gt>get_xml_close_tag();>

Returns closing xml tag for element.

Designed to be used in conjunction with C<get_xml_open_tag> method.  If element has no potential child elements and no content, then will return no closing tag since C<get_xml_open_tag> will have returned a closed tag, eg. E<lt>ELEMENT/E<gt>.

=cut

	sub get_xml_close_tag { $_[0]->_is_closed_tag() ? "" : "</" . $_[0]->get_name() . ">"; }

=item

B<$el-E<gt>generate_full_xml();>

Generates full xml for element.

Displays all attributes and element content.  If no possible child elements and no current element content, will display closed tag, eg. E<lt>ELEMENT/E<gt>.

Method is recursive.

Parameters (receives): [ 0 = class ] , 1 = element count , 2 = indent/tab count and 3 = output.

Returns: 0 = element count and 1 = output.

=cut

	sub generate_full_xml { 
		my ( $self , $element_count , $indent , $output , @return ) = 
			( shift , shift , shift , shift );
		$element_count = 0 if not defined $element_count;
		$indent = -1 if not defined $indent;
		$output = '' if not defined $output;
		++$element_count;
		for ( 0 .. $indent ) { $output .= "\t"; }
		$output .= $self->get_xml_open_tag( "true" , "true" );
		if ( $self->has_children() ) {
			$output .= "\n";
			++$indent;
			foreach ( $self->get_children() ) {
				( $element_count , $output ) = $_->generate_full_xml( 
													$element_count , 
													$indent , 
													$output
				);
			}
			--$indent;
			for ( 0 .. $indent ) { $output .= "\t"; }
		}
		$output .= $self->get_xml_close_tag() . "\n";
		return ( $element_count , $output );
	}

=item

B<$el-E<gt>get_a_child();>

Returns a single child element matching supplied array index.  Returns C<undef> if index out of bounds.

Parameter: [ 0 = class ] , 1 = index.

=cut

	sub get_a_child { return $_[0]->{children}->{elements}[$_[1]]; }

=item

B<$el-E<gt>show_progress();>

Generates abbreviated xml for element and element children.  Designed to show user's "progress" to date.

Displays only opening tags.  Child elements are displayed only where element is not set to "complete".

Only mandatory attributes are displayed.

Method is recursive.

Parameters (receives): [ 0 = class ] , 1 = output , 2 = stop flag.

Returns: 0  = output.

Algorithm (recursive):

	if self is a stop element
		set stop flag = true
	endif
	add self to output
	if stop flag == true  and  children are complete
		return 'output'
	else
		repeat for all children
			recurse (returns 'output')
		endrepeat
		return 'output'
	endif

	if matching element
		set stop flag = true
	endif
	add to output
	if stop flag true
		if is complete
			if has children
				recurse
			else
				add fragment to output
			endif
		else  # not complete
			
	if has children
		repeat for all children
			recurse (returns 'retval')
			set output = 'retval'
		endrepeat
	endif
	return output

=cut

	sub show_progress { 
		
		my @stop_elements  = qw/ PubType InTextDef AuthorOnly YearOnly /;
		my @start_elements = qw/ CitStyle /;
		
		my ( $self , $output , $stop , $root ) = ( shift , shift , shift , 0 );
		( $output, $root , $stop ) = ( '' , 1 , 0 ) if not defined $output;
		my $class = $self->get_classname();
		# switch: $stop
		# case: '0'
		( $stop == 0 ) and do {
			$stop = 1 if grep /\b$class\b/ , @stop_elements;
		};
		# case: '1'
		( $stop == 1 ) and do {
			$stop = 0 if grep /\b$class\b/ , @start_elements;
		};
		# case: default
		( not grep /$stop/ , qw/ 0 1 / ) and do {
			die "No matching case block for $stop";
		};
		# endswitch
		$output .= $self->get_xml_brief_tag();
		if ( $stop && $self->children_are_complete() ) {
			# Check for special case where last child still incomplete
			if ( $self->has_children() ) {
				if ( $self->has_incomplete_descendent() ) {
					my $second_last = $self->max_child_index() - 1;
					for ( 0 .. $second_last ) {
						$output .= $self->get_a_child( $_ )->get_xml_brief_tag();
					}
					$output .= $self->get_last_child()->show_progress();
				}
			}
		} else {
			if ( $self->has_children() ) {
				foreach ( $self->get_children() ) {
					$output = $_->show_progress( $output , $stop );
				}
			}
		}
		$output.= "\n" if $root;
		return $output;

	}

=item

B<$el-E<gt>generate_brief_xml();>

Generates abbreviated xml for element.  Designed to show user's "progress" to date.

Displays only opening tags.  Child elements are displayed only where element is not set to "complete".

Only mandatory attributes are displayed.

Method is recursive.

Parameters (receives): [ 0 = class ] and 1 = output.

Returns: 0  = output.

=cut

	sub generate_brief_xml { 
		my ( $self , $output , $root ) = ( shift , shift , 0 );
		( $output, $root ) = ( '' , 1 ) if not defined $output;
		$output .= $self->get_xml_brief_tag();
		if ( ( not $self->is_childless_model() ) 
				&& ( not $self->children_are_complete ) 
				&& $self->has_children() ) {
			foreach ( $self->get_children() ) {
				$output = $_->generate_brief_xml( $output );
			}
		}
		$output.= "\n" if $root;
		return $output;
	}

=item

B<$el-E<gt>get_full_tag();>

Returns opening +/- closing xml tag for element.

Displays all attributes and element content.  If no possible child elements and no current content, will display single closed tag, eg. E<lt>ELEMENT/E<gt>.

=cut

	sub get_full_tag { 
		$_[0]->get_xml_open_tag( "true" , "true" ) . $_[0]->get_xml_close_tag();
	}

=item

B<$el-E<gt>_test_attributes_mandatory();>

Intended as helper method for C<is_creatable>.  Tests that mandatory attributes can be created.

Note: The attributes are not actually added to the element.

Returns boolean.  False value indicates failure adding mandatory attributes and the associated element should be detroyed.

=cut

	sub _test_attributes_mandatory {
		my ( $self , $exit_status ) = ( shift , 1 );
		if ( not defined( $self->get_attr_mandatory() ) ) { return $exit_status; }
		foreach ( $self->get_attr_mandatory() ) {
			my $attribute = $_->new();
			if ( not ref( $attribute ) ) { $exit_status = 0; }
		}
		return $exit_status;
	}

=item

B<$el-E<gt>add_attributes_mandatory();>

Mandatory attributes are immediately added (note: mandatory attributes should have been added during element construction).

Returns boolean.  False value indicates failure adding mandatory attributes and the associated element should be detroyed.

Can be run in 'silent' mode with no output from method.  There are no guarantees about the 'silence' of attributes' C<select_value> method, but all such methods should I<not> generate output.

Parameters: [ 0 = class ] , [ 1 = <text> | 1 ]

=cut

	sub add_attributes_mandatory {
		my ( $self , $silent , $exit_status ) = ( shift , shift , 1 );
		if ( not defined( $self->get_attr_mandatory() ) ) { return $exit_status; }
		foreach ( $self->get_attr_mandatory() ) {
			if ( not $silent ) {
				$self->_display( "\nElement: " . $self->get_full_tag() );
				$self->_display( "\nAdding mandatory attribute(s)." );
				print "\n";
			}
			my $attribute = $_->new();
			if ( not ref( $attribute ) ) {
				$exit_status = 0;
				last;
			}
			$attribute->select_value();
			$self->_push_attribute( $attribute );
		}
		return $exit_status;
	}

=item

B<$el-E<gt>add_attributes_optional();>

The user selects optional attributes to add.  Help is available.  Element feedback is presented as the user proceeds.

=cut

	sub add_attributes_optional {
		my $self = shift;
		if ( not defined( $self->get_attr_optional() ) ) { return; }
		while ( 1 ) {
			my @attributes = $self->list_selectable_attributes();
			if ( not @attributes ) { return; }
			push( @attributes , "[HELP]" , "[DONE]" );
			$self->_display( "\nElement: " . $self->get_full_tag() );
			$self->_display( "\nAdding optional attribute(s)." );
			print "\n";
			my $choice = undef;
	        while ( 1 ) {
	            $choice = $self->_input_choice( "Select attribute to add:" , @attributes );
	            if ( defined( $choice ) ) { last; }
	            print "Invalid choice.  Sorry, please try again.\n";
	        }
			if ( $choice eq "[DONE]" ) { return; }
			if ( $choice eq "[HELP]" ) { 
				$self->_view( "HELP SCREEN: SELECTABLE ATTRIBUTES" ,
					$self->_build_attribute_help(
						$self->list_selectable_attributes() ) );
				next; }
			my $attribute = $choice->new();
			$attribute->select_value();
			$self->_push_attribute( $attribute );
		}
	}

=item

B<$el-E<gt>mandatory_attributes_complete();>

Returns boolean indicating whether mandatory attributes have been added.

Indicated by a data member which is set after C<add_attributes()> method called.

=cut

sub mandatory_attributes_complete { return $_[0]->{attributes}->{man_complete}; }

=item

B<$el-E<gt>optional_attributes_complete();>

Returns boolean indicating whether optional attributes have been added.

Indicated by a data member which is set after C<add_attributes()> method called.

=cut

sub optional_attributes_complete { return $_[0]->{attributes}->{opt_complete}; }

=item

B<$el-E<gt>add_attributes();>

First adds the mandatory attributes and then gives user chance to add optional attributes.

Returns boolean indicating whether attributes added successfully.  Only mechanism of failure occurs when element requires mandatory attribute such as ROLE which cannot be added because maximum number of ROLE attributes already exist for this PUBTYPE.

=cut

	sub add_attributes {
		my ( $self , $exit_status ) = ( shift , 1 );
		return 1 if $self->mandatory_attributes_complete() and $self->optional_attributes_complete();
		if ( not $self->mandatory_attributes_complete() ) {
			my $exit_status = $self->add_attributes_mandatory();
			$self->set_mandatory_attributes_complete( 1 );
		}
		if ( not $self->optional_attributes_complete() ) {
			if ( $exit_status ) { $self->add_attributes_optional(); }
			$self->set_optional_attributes_complete( 1 );
		}
		return $exit_status;
	}

=item

B<$el-E<gt>add_child();>

Adds a child to the current element.

Parameters: [ 0 = class ] , 1 = child element (object).

=cut

	sub add_child { push( @{ $_[0]->{children}->{elements} } , $_[1] ); }

=item

B<$el-E<gt>_select_next_child_order();>

Helper method for method C<select_next_child>.  Used when element has a modal content model.  Help is provided where appropriate.

Returns two element list: 0 = message indicating whether selection was forced by the content model or chosen by user , 1 = element class name.

Note: If user makes no selection, and content model forces none, an C<undef> element class name is returned along with an informative message.

When content model is exhausted the content flag -- $self-E<gt>{children}-E<gt>{complete} -- is set to true.

The algorithm for this subroutine is the nastiest in the program.  I include it here in full for future occasions when I may have to debug.

	if no current child elements
		set OUT_OF_CHILDREN = true
	else
		set OUT_OF_CHILDREN = false
	endif

	begin loop
		if OUT_OF_CHILDREN == true
			switch require
				case +
					if PREV != element
						EXIT: mandatory element
					else  # ( PREV_CHILD == element )
						ASK: repeat element
						if yes
							EXIT: chosen element
						else  # ( no )
							if last element
								set children_full flag = true
								EXIT: END
							else  # ( not last element )
								advance element
							endif
						endif
					endif
				endcase
				case 1
					if last element
						set children_full flag = true
					endif
					EXIT: mandatory element
				endcase
				case ?
					ASK: add element
					if yes
						EXIT: chosen element
					else  # ( no repeat )
						if last element
							set children_full flag = true
							EXIT: END
						else  # ( not last element )
							advance element
						endif
					endif
				endcase
			endswitch
		else  # ( OUT_OF_CHILDREN == false )
			if child == element
				switch require
					case ? | 1
						if last child
							if last element
								set children_full flag = true
								EXIT: END
							else  # ( not last element )
								advance element
								set OUT_OF_CHILDREN = true
							endif
						else  # ( not last child )
							if last element
								ERROR: exhausted content model but got $child
							else  # ( not last element )
								advance element
								set PREV_CHILD = null
								advance child
							endif
						endif
					endcase
					case +
						if last child
							ASK: repeat element
							if yes
								EXIT: optional element
							else  # ( no repeat )
								if last element
									set children_full flag = true
									EXIT: END
								else  # ( not last element )
									advance element
									set OUT_OF_CHILDREN = true
								endif
							endif
						else  # ( not last child )
							set PREV_CHILD = child
							advance child
						endif
					endcase
				endswitch
			else  # ( child != element )
				switch require
					case +
						if last element
							ERROR: expecting $element, got $child
						else  # ( not last element )
							if PREV_CHILD == element
								set PREV_CHILD = null
								advance element
							else  # ( PREV_CHILD != element )
								ERROR: expecting $element, got $child
							endif
						endif
					endcase
					case ?
						if last element
							ERROR: content model exhausted, no match for $child
						else  # ( not last element )
							advance element
						endif
					endcase
					case 1
						ERROR: expecting $element, got $child
					endcase
				endswitch
			endif
		endif
	end loop

Warning: In a number of places in this subroutine the program can C<die>.  This occurs when a mismatch between the child element model and the actual child elements is detected.  If the mismatch truly exists it is a coding error since the algorithm should force child elements to match the content model.  If the mismatch is not real the code has erred in analysing the data.  Either way, it is an unrecoverable error and the algorithm/code must be corrected.

=cut

	sub _select_next_child_order {
		my ( $self ,  $msg_required , $msg_optional , $msg_add , $msg_repeat ) = 
			( shift , shift , shift , shift , shift );
		my ( $out_of_children , @selected , $question ) = ( 0 );
		my ( $element_index , $child_index , $previous_child ) = ( 0 , 0 , "" );
		if ( $self->is_childless_model() ) { 
			return ( "Childless model content." , undef );
		}
		if ( $self->max_child_index() < 0 ) { $out_of_children = 1; }
		while ( 1 ) {
			my ( $element , $require ) = @{ $self->{model_order}[$element_index] };
			my $child = ( $self->max_child_index() >= 0 ) 
					? $self->{children}->{elements}[$child_index]->get_classname() 
					: '';
			if ( $out_of_children ) {
				# SWITCH: $require
				# case: '+'
				( $require eq '+' ) and do {
					if ( $element ne $previous_child ) {
						@selected = ( 
							sprintf( $msg_required , $element->new()->get_name() ) ,
							$element 
						);
						last;
					} else {
						$question = sprintf(
							$msg_repeat , 
							$element->new()->get_name() , 
							$element->new()->get_help() );
						if ( $self->_input_confirm( $question ) ) {
							@selected = ( 
								sprintf( $msg_optional , 
									$element->new()->get_name() ) ,
								$element
							);
							last;
						} else {
							if ( $self->is_last_element( $element_index ) ) {
								@selected = ( "Content model exhausted." , undef );
								$self->set_complete( 1 );
								last;
							} else {
								++$element_index;
							}
						}
					}
				};
				# case: '1'
				( $require eq '1' ) and do {
					@selected = ( 
						sprintf( $msg_required , $element->new()->get_name() ) ,
						$element 
					);
					if ( $self->is_last_element( $element_index ) ) {
						$self->set_complete( 1 );
					}
					last;
				};
				# case: '?'
				( $require eq '?' ) and do {
					$question = sprintf( $msg_add , 
						$element->new()->get_name() , 
						$element->new()->get_help() 
					);
					if ( $self->_input_confirm( $question ) ) {
						@selected = ( 
							sprintf( $msg_optional , $element->new()->get_name() ) ,
							$element );
						if ( $self->is_last_element( $element_index ) ) {
							$self->set_complete( 1 );
						}
						last;
					} else {
						if ( $self->is_last_element( $element_index ) ) {
							@selected = ( "Content model exhausted." , undef );
							$self->set_complete( 1 );
							last;
						} else {
							++$element_index;
						}
					}
				};
				# case: default
				( not grep $require eq $_ , qw/ ? 1 + / ) && do {
					die "Fatal error: No matching case block";
				};
				# ENDSWITCH
			} else {  # $out_of_children == false
				if ( $child eq $element ) {
					# SWITCH: $require
					# case: '?' , '1'
					( grep $require eq $_ , qw/ ? 1 / ) and do {
						if ( $self->is_last_child( $child_index ) ) {
							if ( $self->is_last_element( $element_index ) ) {
								@selected = ( "Content model exhausted." , undef );
								$self->set_complete( 1 );
								last;
							} else {
								++$element_index;
								$out_of_children = 1;
							}
						} else {
							if ( $self->is_last_element( $element_index ) ) {
								die "Fatal error: Exhausted content model, got $child";
							} else {
								++$element_index;
								$previous_child = '';
								++$child_index;
							}
						}
					};
					# case: '+'
					( $require eq '+' ) and do {
						if ( $self->is_last_child( $child_index ) ) {
							$question = sprintf( $msg_repeat , 
								$element->new()->get_name() , 
								$element->new()->get_help() 
							);
							if ( $self->_input_confirm( $question ) ) {
								@selected = ( 
									sprintf( $msg_optional , 
										$element->new()->get_name() ) , 
									$element );
								last;
							} else {
								if ( $self->is_last_element( $element_index ) ) {
									@selected = ( "Content model exhausted." , undef );
									$self->set_complete( 1 );
									last;
								} else {
									++$element_index;
									$out_of_children = 1;
								}
							}
						} else {  # not last child
							$previous_child = $child;
							++$child_index;
						}
					};
					# case: default
					( not grep $require eq $_ , qw/ ? 1 + / ) && do {
						die "Fatal error: No matching case block";
					};
					# ENDSWITCH
				} else {  # child != element
					# SWITCH: $require
					# case: '+'
					( $require eq '+' ) and do {
						if ( $self->is_last_element( $element_index ) ) {
							die "Fatal error: Expecting $element, got $child";
						} else {
							if ( $previous_child eq $element ) {
								++$element_index;
							} else {
								die "Fatal error: Expecting $element, got $child";
							}
						}
					};
					# case: '?'
					( $require eq '?' ) and do {
						if ( $self->is_last_element( $element_index ) ) {
							die "Fatal error: Exhausted content model, no match for $child";
						} else {
							++$element_index;
						}
					};
					# case: '1'
					( $require eq '1' ) && do {
						die "Fatal error: Expecting $element, got $child";
					};
					# case: default
					( not grep $require eq $_ , qw/ ? 1 + / ) && do {
						die "Fatal error: No matching case block";
					}; 
					# ENDSWITCH
				}
			}
		}
		return @selected;
	}

=item

B<$el-E<gt>list_selectable_children_noorder();>

Used when selecting child elements when following the unordered model.

Returns a list of all optional elements available for selection.  List consists of all elements attributes listed in 'optional' list I<minus> all attributes already selected.

=cut

	sub list_selectable_children_noorder {
		my ( $self , @children ) = ( shift );
		foreach ( $self->get_noorder_list() ) {
			if ( $_->is_creatable() ) { push( @children , $_ ); }
		}
		if ( $self->max_child_index() >= 0 ) {
			if ( $self->{children}->{elements}[$self->max_child_index()]->get_classname() ne $self->get_noorder_separator() ) {
				push( @children , $self->get_noorder_separator() );
			}
		}
		return @children;
	}

=item

B<$el-E<gt>_build_element_help();>

Helper method for method C<_select_next_child_noorder>.  Takes as parameter a list of elements for which help screen is being built.

=cut

	sub _build_element_help {
		my ( $self , @elements , $help ) = ( shift , @_ );
		foreach my $element ( @elements ) { 
			$help .= $element->new()->get_name() . "\n";
			$help .= $element->new()->get_help() . "\n\n";
		}
		return $help;
	}

=item

B<$el-E<gt>_select_next_child_noorder();>

The user selects optional child element to add.

Helper method for 'select_next_child'.

Returns two element list: 0 = message indicating whether selection was forced by the content model or chosen by user , 1 = element class name.

Note: If user chooses to finish child element selection, C<undef> element class name is returned along with an informative message.

When child element selection is finished the content flag -- $self-E<gt>{children}-E<gt>{complete} -- is set to true.

Help is available.

No option to exit until at least one element has been selected.

Cannot select two C<Separator> elements consecutively.

=cut

	sub _select_next_child_noorder {
		my ( $self , $msg_optional , $choice , @selected ) = ( shift , shift );
		if ( $self->is_childless_model() ) { 
			return ( "Childless model content." , undef );
		}
		while ( 1 ) {
			my @add_options = qw/ [DELETE] [DONE] /;
			my @menu = $self->list_selectable_children_noorder();
			push( @menu , "[HELP]" );
			if ( $self->max_child_index() >= 0 ) { push( @menu , @add_options ); }
			$choice = undef;
	        while ( 1 ) {
	            $choice = $self->_input_choice( "Select child element to add:" , @menu );
	            if ( defined( $choice ) ) { last; }
	            print "Invalid choice.  Sorry, please try again.\n";
	        }
			if ( $choice eq "[HELP]" ) { 
				$self->_view( "HELP SCREEN: SELECTABLE ELEMENTS" ,
					$self->_build_element_help(
						$self->list_selectable_children_noorder() ) );
				next;
			}
			# SWITCH: $choice
			# case: '[DELETE]'
			( $choice eq '[DELETE]' ) and do {
				@selected = ( "User wishes to delete." , "[DELETE]" );
				last;
			};
			# case: '[DONE]'
			( $choice eq '[DONE]' ) and do {
				@selected = ( "User has finished optional element selection." , undef );
				$self->set_complete( 1 );
				last;
			};
			# case: default ( = <element> )
			( not grep $choice eq $_ , @add_options ) and do {
				@selected = ( 
					sprintf( $msg_optional , 
						$choice->new()->get_name() ) ,
					$choice
				);
				last;
			};
			# ENDSWITCH
		}
		return @selected;
	}

=item

B<$el-E<gt>select_next_child();>

The user selects a child element to add.  Help is available.

Returns two element list: 0 = text indicating whether mandatory or user choice ,
                          1 = element class name.

Note: If user makes no selection, and content model forces none, an C<undef> element class name is returned along with an informative message.

The algorithm for this subroutine is the nastiest in the program.  I include it here in full for future occasions when I may have to debug.

=cut

	sub select_next_child {
		my ( $self , @selected ) = ( shift );
		my $msg_required = "Element %s is required here.\n";
		my $msg_optional = "User selected optional element %s.\n";
		my $msg_add      = "Do you want to add child element %s ?\n\n%s";
		my $msg_repeat   = "Do you want to add another element %s ?\n\n%s";
		if ( $self->is_ordered_child_model() ) {
			@selected = $self->_select_next_child_order( $msg_required , $msg_optional , $msg_add , $msg_repeat );
		}
		if ( $self->is_unordered_child_model() ) {
			@selected = $self->_select_next_child_noorder( $msg_optional );
		}
		return @selected;
	}

=item

B<$el-E<gt>is_copyable();>

Returns boolean indicating whether element can be copied.

Based on value of $self-E<gt>{copyable}.

=cut

	sub is_copyable { return $_[0]->{copyable}; }

=item

B<$el-E<gt>copy();>

Attempts to copy element.  Returns either a reference to a copied element or an error message.

Can test for success using C<ref> function.

Does not copy attributes.  Attributes requiring user input during creation are not copyable (this includes Type_PubType, Role_UserDef, Role_Misc and Role_Link).

Does not copy child elements.  For that, use the C<duplicate> method.

=cut

	sub copy { return $_[0]->get_classname()->new(); }

=item

B<$el-E<gt>has_attributes();>

Returns boolean indicating whether element has attributes.

=cut

	sub has_attributes { 
		my ( $self , $result ) = ( shift , 0 );
		if ( defined( $self->{attributes}->{selected} ) ) {
			if ( $#{ $self->{attributes}->{selected} } >= 0 ) { 
				$result = 1;
			}
		}
		return $result;
	}

=item

B<$el-E<gt>has_children();>

Returns boolean indicating whether element has children.

=cut

	sub has_children { 
		my ( $self , $result ) = ( shift , 0 );
		if ( defined( $self->{children}->{elements} ) ) {
			if ( $self->max_child_index() >= 0 ) { 
				$result = 1;
			}
		}
		return $result;
	}

=item

B<$el-E<gt>_push_attribute();>

Adds attribute to element.

Parameters: [ 0 = class ] , 1 = attribute object.

=cut

	sub _push_attribute { push @{ $_[0]->{attributes}->{selected} } , $_[1]; }

=item

B<$el-E<gt>duplicate();>

Returns either a reference to a duplicate element or an error message.

Copies all attributes, child elements and element content.

=cut

	sub is_duplicatable {
		my ( $self , $result ) = ( shift , 0 );
		{
			if ( ref( $self->duplicate() ) ) { $result = 1; }
		}
		return $result;
	}

=item

B<$el-E<gt>duplicate();>

Returns either a reference to a duplicate element or an error message.

Copies all attributes, child elements and element content.

=cut

	sub duplicate {
		my ( $self , $success , $message , $copy ) = ( shift , 1 );
		if ( not $self->is_copyable() ) {
			return sprintf(
				"(Sub)Element %s is not copyable." , 
				$self->get_name() 
			);
		}
		$copy = $self->copy();
		# first, copy attributes
		if ( $self->has_attributes() ) {
			foreach my $at ( $self->get_attr_selected() ) {
				my $attribute = $at->get_classname()->new();
				if ( ref( $attribute ) ) {
					if ( $attribute->get_value() ) {
						# autogenerated - don't change
					} else {
						if ( $at->get_value() ) {
							$attribute->_set_value( $at->get_value() );
						}
					}
				} else {
					return $attribute;
				}
				$copy->_push_attribute( $attribute );
			}
		}
		# next, copy element value
		if ( $self->is_content_allowed() ) {
			if ( $self->get_value() ) {
				$copy->_set_value( $self->get_value() );
			}
		}
		# now, recurse for child elements
		if ( $self->has_children() ) {
			foreach my $ch ( $self->get_children() ) {
				my $child = $ch->duplicate();
				if ( ref( $child ) ) {
					$copy->add_child( $child );
				} else {
					return $child;
				}
			}
		}
		return $copy;
	}

=item

B<$el-E<gt>is_ordered_child_model();>

Returns boolean indicating nature of child element content model.

=cut

	sub is_ordered_child_model { $_[0]->get_order(); }

=item

B<$el-E<gt>is_unordered_child_model();>

Returns boolean indicating nature of child element content model.

=cut

	sub is_unordered_child_model { $_[0]->get_noorder_list(); }

=item

B<$el-E<gt>is_childless_model();>

Returns boolean indicating nature of child element content model.

=cut

	sub is_childless_model {
		my ( $self , $result ) = ( shift , 1 );
		if ( $self->is_ordered_child_model() ) { $result = 0; }
		if ( $self->is_unordered_child_model() ) { $result = 0; }
		return $result;
	}

=item

B<$el-E<gt>get_last_parent();>

Return reference to last parent element.

Parameters: [ 0 = class ] , 1 = originator flag (true if not originating element)

Recursive algorithm:

	if child elements
		recurse last child (returns 'retval')
		if 'retval' is element
			return 'retval'
		else
			return self
		endif
	else
		if recursed element
			return undef
		else
			return self
		endif
	endif

=cut

	sub get_last_parent {
		my ( $self , $recursed , $result ) = ( shift , shift );
		if ( $self->has_children() ) { 
			my $return = $self->get_last_child()->get_last_parent( 1 );
			return defined( $return ) ? $return : $self;
		} else {
			return $recursed ? undef : $self;
		}
	}

=item

B<$el-E<gt>get_last_element();>

Return reference to last element.

Recursive algorithm:

	if child elements
		recurse last child (returns 'retval')
		return 'retval'
	else
		return self
	endif

=cut

	sub get_last_element { $_[0]->has_children() ? 
						   $_[0]->get_last_child()->get_last_element() : $_[0]; }

=item

B<$el-E<gt>delete_last_child();>

Delete last child of this element.

Returns element.

=cut

	sub delete_last_child {
		my $self = shift;
		return pop @{ $self->{children}->{elements} };
	}

=item

B<$el-E<gt>delete_last_element();>

Delete last element.

Return deleted element.

=cut

	sub delete_last_element {
		my $self = shift;
		return $self->get_last_parent()->delete_last_child();
	}

=item

B<$el-E<gt>has_incomplete_descendent();>

Returns boolean indicating whether any descendent of element is incomplete.

=cut

	sub has_incomplete_descendent { 
		my ( $self , $result ) = ( shift , shift );
		return 1 if not $self->children_are_complete();
		if ( $self->has_children() ) {
			foreach ( $self->get_children() ) {
				return 1 if $_->has_incomplete_descendent();
			}
		}
	}
			
=item

B<$el-E<gt>get_last_incomplete();>

Return reference to last element in tree that is incomplete.

Returns C<undef> if all elements complete.

Recursive algorithm:

	if child elements
		recurse last child (returns 'retval')
		if 'retval' is element
			return 'retval'
		endif
	else
		if not complete
			return self
		else
			return undef
		endif
	endif

=cut

	sub get_last_incomplete {
		my ( $self , $recursed ) = ( shift , shift );
		if ( $self->has_children() ) { 
			my $return = $self->get_last_child()->get_last_incomplete( 1 );
			return ref( $return ) ? $return : $self->children_are_complete() ? undef : $self;
		} else {
			return $self->children_are_complete() ? undef : $self;
		}
	}

=item

B<$el-E<gt>is_named();>

Returns boolean indicating whether element has display data member set to "name".

=cut

	sub is_named { defined $_[0]->{display} && $_[0]->{display} eq "name" ? 1 : 0; }

=item

B<$el-E<gt>get_last_named();>

Return reference to last element in tree with display data member set to 'name'.

=cut

	sub get_last_named {
		my ( $self , $recursed ) = ( shift , shift );
		if ( $self->has_children() ) { 
			my $return = $self->get_last_child()->get_last_named( 1 );
			return ref( $return ) ? $return : $self->is_named() ? $self : undef;
		} else {
			return $self->is_named() ? $self : undef ;
		}
	}

=item

B<$el-E<gt>get_style_name();>

Finds StyleName element and returns element content.

If unsuccessful, return C<undef>.

=cut

	sub get_style_name {
		my $self = shift;
		return $self->get_value() if $self->get_classname() eq "StyleName";
		if ( $self->has_children() ) { 
			foreach ( $self->get_children() ) { return $_->get_style_name(); }
		} else { return undef; }
	}

=item

B<$el-E<gt>get_most_recent();>

Return reference to last element in tree of supplied element class.

If no element found, return C<undef>.

=cut

	sub get_most_recent {
		my ( $self , $class , $most_recent , $just_found , $return) = 
			( shift , shift , shift );
		if ( $self->has_children() ) { 
			foreach ( $self->get_children() ) {
				$return = $_->get_most_recent( $class , $most_recent );
				$just_found = $return if ref( $return );
			}
		}
		return $most_recent if ref( $most_recent );
		return $just_found if ref( $just_found );
		return ( $self->get_classname() eq $class ) ? $self : undef;
	}

=item

B<$el-E<gt>reset_all_counters();>

Resets all role level counters

Called by PubType, AuthorOnly, YearOnly and InTextDef during element creation.

=cut

	sub reset_all_counters {
		foreach ( qw/ Role_AuthorList Role_PubDate Role_Title Role_UserDef Role_Misc Role_Link  / ) { 
			$_->reset_counter();
		}
	}

=item

B<$el-E<gt>named_in_summary();>

Boolean indicating whether to include element in summary.

=cut

	sub named_in_summary { 
		if ( $_[0]->get_summary() ) { ( $_[0]->get_summary() eq "name" ) ? 1 : 0; }
	}

=item

B<$el-E<gt>content_in_summary();>

Boolean indicating whether to include raw element content in summary.  Currently only applies to SEPARATOR.

=cut

	sub content_in_summary {
		if ( $_[0]->get_summary() ) { ( $_[0]->get_summary() eq "content" ) ? 1 : 0; }
	}

=item

B<$el-E<gt>has_bracketing_child();>

Boolean indicating whether element has any child elements whose content will "bracket" this element's content in output.  Currently only applies to PRECEEDING and FOLLOWING.

=cut

	sub has_bracketing_child { 
		my ( $self , $return ) = ( shift , 0 );
		if ( $self->has_children() ) {
			foreach ( $self->get_children() ) { 
				my $class = $_->get_classname();
				$return = 1 if grep /\b$class\b/ , qw/ Preceeding Following /;
			}
		}
		return $return;
	}

=item

B<$el-E<gt>generate_summary();>

Generates substantial portion of style summary.  Called by UI method 'write_summary'.

Parameters: [ 0 = class ] , 1 = previous output.

Returns: 0 = current output.

Since this method is only called on a complete style, we can make assumptions based on the style following the DTD.

Recursive algorithm.

	switch <element class>
		case 'StyleName'
			add to output: style name
		endcase
		case 'RefStyle'
			add to output: header = "Bibliography Style"
		endcase
		case 'PubStyle'
			add to output: header giving publication type
		endcase
		case 'CitStyle'
			add to output: header = "Citation Style"
		endcase
		case 'InTextDef'
			add to output: header = Author and Year
		endcase
		case 'AuthorOnly'
			add to output: header = Author
		endcase
		case 'YearOnly'
			add to output: header = Year
		endcase
	endswitch
	if summary flag == name
		if bracketing child element(s)
			set bracket flag = true
		endif
		if bracket flag == true
			add to output: opening brackets
		endif
		if bracket flag == true
			if left bracketing element present
				print left bracketing element contents
			endif
		endif
		add to output: left marker
		add to output: element name
		repeat for all attributes
			if attribute summary flag == true
				add to output: attribute value (italicised)
			endif
		endrepeat
		add to output: right marker
	endif
	if have children
		repeat for all children	
			** recurse (send output, receive output as 'retval')
		endrepeat
	endif
	return output

=back

=cut

	sub generate_summary {
		my ( $self , $output , $div , $recurse ) = 
			( shift , shift , shift , shift );
		my ( $left_bracket , $right_bracket ) = (  "{"   ,  "}" );
		my ( $left_marker  , $right_marker  ) = ( "<i>" , "</i>" );
		my @left  = qw/ Preceeding /;
		my @right = qw/ Following  /;
		# Headings
		my $class = $self->get_classname();
		# SWITCH: class
		# case: 'StyleName'
		( $class eq 'StyleName' ) and do {
			my $stylename = sprintf( "RefDB Style: %s" , $self->get_value() );
			$output .= sprintf( "<html>\n<head>\n<title>%s</title>\n</head>\n<body>\n<h1>%s</h1>\n<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\">\n<tr>\n<td><i>ELEMENT</i></td>\n<td>&nbsp;&nbsp;&nbsp;&nbsp;</td>\n<td>A major component of the bibliography/citation.</td>\n</tr>\n<tr>\n<td><u>ATTRIBUTE</u></td>\n<td>&nbsp;&nbsp;&nbsp;&nbsp;</td>\n<td>An element attribute.</td>\n</tr>\n<tr>\n<td>{}</td>\n<td>&nbsp;&nbsp;&nbsp;&nbsp;</td>\n<td>Encloses text/punctuation that appears only if associated element is present.</td>\n</tr>\n<tr>\n<td><b>bolded</b></td>\n<td>&nbsp;&nbsp;&nbsp;&nbsp;</td>\n<td>Text/punctuation that always appears, regardless of which elements are present.</td>\n</tr>\n</table>\n" , $stylename , $stylename );
		};
		# case: 'RefStyle'
		( $class eq 'RefStyle' ) and do {
			$output .= "<hr>\n<h2>Reference Style</h2>\n";
		};
		# case: 'PubType'
		( $class eq 'PubType' ) and do {
			if ( $div ) { $output .= "</div>\n"; }
			my @pubtype = $self->get_publication_type();
			$output .= sprintf( "<h3>Publication type %s: %s</h3>\n<div>" , $pubtype[1] , $pubtype[0] );
			$div = 1;
		};
		# case: 'CitStyle'
		( $class eq 'CitStyle' ) and do {
			$output .= "</div>\n<hr>\n<h2>Citation Style</h2>\n<div>";
		};
		# case: 'InTextDef'
		( $class eq 'InTextDef' ) and do {
			$output .= "</div>\n<h3>Citation Style: Author/Year</h3>\n<div>";
		};
		# case: 'AuthorOnly'
		( $class eq 'AuthorOnly' ) and do {
			$output .= "</div>\n<h3>Citation Style: Author only</h3>\n<div>";
		};
		# case: 'YearOnly'
		( $class eq 'YearOnly' ) and do {
			$output .= "</div>\n<h3>Citation Style: Year only</h3>\n<div>";
		};
		# case: 'BibStyle'
		( $class eq 'BibStyle' ) and do {
			$output .= "</div>\n<hr>\n<h2>Bibliography Style</h2>\n<div>";
			if ( $self->has_attributes() ) {
				for ( 0 .. $self->max_attribute_index() ) {
					$output .= sprintf( 
						"%s: '%s'</div>\n<div>" , 
						$self->{attributes}->{selected}[$_]->get_name() , 
						$self->{attributes}->{selected}[$_]->get_value()
					);
				}
			} else {
				$output .= "<i>No styling changes specified</i></div>\n<div>";
			}
		};
		# case: 'BiblioTitle'
		( $class eq 'BiblioTitle' ) and do {
			$output .= sprintf( 
				"%s: '%s'</div>\n<div>" , 
				$self->get_name() ,
				$self->get_value()
			);
		};
		# ENDSWITCH
		# Major element - include name/attributes/bracketing punctuation
		if ( $self->named_in_summary() ) {
			my $bracketed = 1 if $self->has_bracketing_child();
			# left bracket
			if ( $bracketed ) { $output .= $left_bracket; }
			# preceeding punctuation
			if ( $self->has_children() ) {
				foreach ( $self->get_children() ) {
					my $child = $_->get_classname();
					$output .= $_->get_value() if grep /\b$child\b/ , @left;
				}
			}
			# element name
			$output .= $left_marker;
			$output .= $self->get_name();
			$output .= $right_marker;
			# element attributes
			foreach ( $self->get_attr_selected() ) {
				if ( $_->get_summary() ) {
					$output .= sprintf( " <u>%s</u>" , $_->get_value() );
				}
			}
			# following punctuation
			if ( $self->has_children() ) {
				foreach ( $self->get_children() ) {
					my $child = $_->get_classname();
					$output .= $_->get_value() if grep /\b$child\b/ , @right;
				}
			}
			# right bracket
			if ( $bracketed ) { $output .= $right_bracket; }
		}
		
		# Separator element - include raw content
		if ( $self->content_in_summary() && $self->get_value() ) {
			$output .= sprintf( "<b>%s</b>" , $self->get_value() );
		}
		
		# Recurse
		if ( $self->has_children() ) {
			foreach ( $self->get_children() ) {
				$output = $_->generate_summary( $output , $div , 1 );
			}
		}
		
		# If original/final element then complete html
		$output .= "\n</div>\n<hr>\n</body>\n</html>" if not $recurse;
		
		# All done
		return $output;		
	}

=head2 Elements: List of Classes

To see a list of attribute classes use the C<document-dtd-entities> utility that shipped with this program.  It extracts attribute and element properties from this script, assembles and formats them into a single html document.

All elements descend from one of three ancestor classes depending on their content model:

=over

=item

B<_Model_Childless>

Elements descended from this class have a content model specifying no children elements.

=cut

package _Model_Childless;
	use base qw/ _Element /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_order( undef );
		$self->set_noorder( undef );
		$self->set_complete( 1 );
		return $self;
	}

=item

B<_Model_Order>

Elements descended from this class have a content model of the general form:

	( element_A[?|+] , element_B[?|+] , ... )

=cut

package _Model_Order;
	use base qw/ _Element /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_order( undef );
		$self->set_noorder( undef );
		return $self;
	}

=item

B<_Model_Noorder>

Elements descended from this class have a content model of the general form:

	( element_A | element_B , ... ) , element_X? )+

=back

=cut

package _Model_Noorder;
	use base qw/ _Element /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_order( undef );
		$self->set_noorder( { 
			element_list => [ "RefNumber" , "AuthorList" , "PubDate" , "Title" , "JournalName" , "Volume" , "Issue" , "Pages" , "Publisher" , "PubPlace" ] ,
	   		separator    => "Separator" ,
		} );
		return $self;
	}

package StyleSet;
	use base qw/ _Model_Order /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "STYLESET" );
		$self->set_prompt( "Top-level element.  Enables multiple bibliography styles to be stored in one document.  Not needed if single style stored in document/file." );
		$self->set_help( "Top-level element.  Enables multiple bibliography styles to be stored in one document.  Not needed if single style stored in document/file." );
		$self->set_display( "name" );
		$self->set_order( [
			[ "CiteStyle" , "+" ] ,
		] );
		return $self;
	}

	# Class methods
	sub default_copyable { undef; }

package CiteStyle;
	use base qw/ _Model_Order /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "CITESTYLE" );
		$self->set_prompt( "Encloses a bibliography style for a particular journal or publisher." );
		$self->set_help( "Encloses a bibliography style for a particular journal or publisher." );
		$self->set_display( "name" );
		$self->set_order( [
			[ "StyleName" , "1" ] ,
			[ "RefStyle"  , "1" ] ,
			[ "CitStyle"  , "1" ] ,
			[ "BibStyle"  , "1" ] ,
		] );
		return $self;
	}

	# Class methods
	sub default_copyable { undef; }

package StyleName;
	use base qw/ _Model_Childless /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "STYLENAME" );
		$self->set_prompt( "Determines the name of the style" );
		$self->set_help( "This value is the name of the style.  It is case-sensitive, i.e., RefDB can have two different styles called \"Style\" and \"style\".  The style name will be the root of the names given to the style and summary files generated by this utility." );
		$self->set_display( "name" );
		$self->set_content( {
			allowed    => 1 ,
			suggestion => undef ,
			value      => undef ,
		} );
		return $self;
	}

	# Class methods
	sub default_copyable { undef; }

package RefStyle;
	use base qw/ _Model_Order /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "REFSTYLE" );
		$self->set_prompt( "Encloses all elements responsible for the style of the bibliography." );
		$self->set_help( "Encloses all elements responsible for the style of the bibliography." );
		$self->set_display( "name" );
		$self->set_order( [
			[ "PubType" , "+" ] ,
		] );
		return $self;
	}

	# Class methods
	sub default_copyable { undef; }

package PubType;
	use base qw/ _Model_Noorder /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "PUBTYPE" );
		$self->set_prompt( "Refdb allows for 35 different publication types (including BOOK and JOUR) and each can have a different style.  This element encloses all elements responsible for the styling of a single publication type." );
		$self->set_help( "Refdb allows for 35 different publication types (including BOOK and JOUR) and each can have a different style.  This element encloses all elements responsible for the styling of a single publication type." );
		$self->set_display( "name" );
		$self->set_attributes( {
			mandatory => [ "Type_PubType" ] ,
			optional  => undef ,
		} );
		$self->set_noorder( { 
			element_list => [ "RefNumber" , "AuthorList" , "PubDate" , "Title" , "JournalName" , "Volume" , "Issue" , "Pages" , "Publisher" , "PubPlace" , "Serial" , "Address" , "UserDef" , "Misc" , "Notes" , "Abstract" , "Link" ] ,
	   		separator    => "Separator" ,
		} );
		$self->reset_all_counters();
		return $self;
	}

	# Class methods
	sub default_copyable { undef; }

	sub get_publication_type {
		my ( $self , @type ) = ( shift );
		my %_lookup = ( "GEN"     =>  "default" , 
		                "ABST"    =>  "abstract" , 
						"ADVS"    =>  "audiovisual material" , 
						"ART"     =>  "art work" , 
						"BILL"    =>  "resolution/bill" , 
						"BOOK"    =>  "whole book" , 
						"CASE"    =>  "case" , 
						"CHAP"    =>  "book chapter" , 
						"COMP"    =>  "computer program" , 
						"CONF"    =>  "conference proceeding" , 
						"CTLG"    =>  "catalog" , 
						"DATA"    =>  "data file" , 
						"ELEC"    =>  "electronic citation" , 
						"HEAR"    =>  "hearing" , 
						"ICOMM"   =>  "internet communication" , 
						"INPR"    =>  "in press" , 
						"JFULL"   =>  "entire journal/periodical" , 
						"JOUR"    =>  "journal/periodical article" , 
						"MAP"     =>  "map" , 
						"MGZN"    =>  "magazine" , 
						"MPCT"    =>  "motion picture" , 
						"MUSIC"   =>  "music score" , 
						"NEWS"    =>  "newspaper" , 
						"PAMP"    =>  "pamphlet" , 
						"PAT"     =>  "patent" , 
						"PCOMM"   =>  "personal communication" , 
						"RPRT"    =>  "report" , 
						"SER"     =>  "serial--book monograph" , 
						"SLIDE"   =>  "slide" , 
						"SOUND"   =>  "sound recording" , 
						"STAT"    =>  "statute" , 
						"THES"    =>  "thesis/dissertation" , 
						"UNBILL"  =>  "unenacted bill/resolution" , 
						"UNPB"    =>  "unpublished" , 
						"VIDEO"   =>  "video recording" ,
					);
		foreach ( $self->get_attr_selected() ) {
			if ( $_->get_classname() eq "Type_PubType" ) {
				unshift @type , $_->get_value();
				unshift @type , $_lookup{$type[0]};
				last;
			}
		}
		return @type;
	}

package RefNumber;
	use base qw/ _Model_Order /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "REFNUMBER" );
		$self->set_prompt( "Specifies location of reference number (in styles that use numbered bibliography references.  Determines formatting of the number." );
		$self->set_help( "Specifies location of reference number (in styles that use numbered bibliography references.  Determines formatting of the number." );
		$self->set_display( "name" );
		$self->set_summary( "name" );
		$self->set_attributes( {
			mandatory => undef ,
			optional  => [ "Style" ] ,
		} );
		$self->set_order( [
			[ "Preceeding" , "?" ] ,
			[ "Following"  , "?" ] ,
		] );
		return $self;
	}

package Preceeding;
	use base qw/ _Model_Childless /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "PRECEEDING" );
		$self->set_prompt( "Defines character(s) which precede the parent element, eg. 'vol. ' to precede journal volume." );
		$self->set_help( "Defines character(s) which precede the parent element, eg. 'vol. ' to precede journal volume." );
		$self->set_display( "content" );
		$self->set_summary( "left" );
		$self->set_content( {
			allowed    => 1 ,
			suggestion => undef ,
			value      => undef ,
		} );
		return $self;
	}

	# Class-specific
	sub _is_valid_value {
		if ( defined( $_[1] ) && ( $_[1] ne "" ) ) {
			1;
		} else {
			print "Invalid value.  Sorry, please try again.\n";
			0;
		}
	}

package Following;
	use base qw/ _Model_Childless /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "FOLLOWING" );
		$self->set_prompt( "Define character(s) which follow the parent element, eg. follow publisher with '.' to end reference." );
		$self->set_help( "Define character(s) which follow the parent element, eg. follow publisher with '.' to end reference." );
		$self->set_display( "content" );
		$self->set_summary( "right" );
		$self->set_content( {
			allowed    => 1 ,
			suggestion => undef ,
			value      => undef ,
		} );
		return $self;
	}

	# Class-specific
	sub _is_valid_value {
		if ( defined( $_[1] ) && ( $_[1] ne "" ) ) {
			1;
		} else {
			print "Invalid value.  Sorry, please try again.\n";
			0;
		}
	}

package AuthorList;
	use base qw/ _Model_Order /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "AUTHORLIST" );
		$self->set_prompt( "Specifies location of an author list [can be any of ris elements: AU|A1, A2, A3] in citation/reference.  Contains elements and attributes determining the formatting of the list of authors." );
		$self->set_help( "Specifies location of an author list [can be any of ris elements: AU|A1, A2, A3] in citation/reference.  Contains elements and attributes determining the formatting of the list of authors." );
		$self->set_display( "name" );
		$self->set_summary( "name" );
		$self->set_attributes( {
			mandatory => [ "Role_AuthorList" ] ,
			optional  => [ "AlternateStyle" , "AlternateText_AuthorList" , "Style" ] ,
		} );
		$self->set_order( [
			[ "Preceeding"       , "?" ] ,
			[ "Following"        , "?" ] ,
			[ "AbbreviateFirst"  , "?" ] ,
			[ "AbbreviateSubseq" , "?" ] ,
			[ "Aempty"           , "?" ] ,
			[ "Asame"            , "?" ] ,
			[ "AuthorSeps"       , "1" ] ,
			[ "AuthorNames"      , "1" ] ,
			[ "Text"             , "?" ] ,
		] );
		return $self;
	}

package AbbreviateFirst;
	use base qw/ _Model_Childless /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "ABBREVIATEFIRST" );
		$self->set_prompt( "Determines how many authors will be listed in the first citation or reference of a unique list of authors.  Also specifies what text to use in place of excess authors, eg. 'et al.'." );
		$self->set_help( "Determines how many authors will be listed in the first citation or reference of a unique list of authors.  Also specifies what text to use in place of excess authors, eg. 'et al.'." );
		$self->set_display( undef );
		$self->set_attributes( {
			mandatory => [ "DisplayAuthor" , "MaxAuthor" ] ,
			optional  => [ "Style" ] ,
		} );
		$self->set_content( {
			allowed    => 1 ,
			suggestion => "et al." ,
			value      => undef ,
		} );
		return $self;
	}

package AbbreviateSubseq;
	use base qw/ _Model_Childless /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "ABBREVIATESUBSEQ" );
		$self->set_prompt( "Determines how many authors will be listed in subsequent citations or references of a unique list of authors.  Also specifies what text to use in place of excess authors, eg. 'et al.'." );
		$self->set_help( "Determines how many authors will be listed in subsequent citations or references of a unique list of authors.  Also specifies what text to use in place of excess authors, eg. 'et al.'." );
		$self->set_display( undef );
		$self->set_attributes( {
			mandatory => [ "DisplayAuthor" , "MaxAuthor" ] ,
			optional  => [ "Style" ] ,
		} );
		$self->set_content( {
			allowed    => 1 ,
			suggestion => "et al." ,
			value      => undef ,
		} );
		return $self;
	}

package Aempty;
	use base qw/ _Model_Childless /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "AEMPTY" );
		$self->set_prompt( "Determines what text is used as author name when none is supplied by the reference, eg. 'Anonymous'." );
		$self->set_help( "Determines what text is used as author name when none is supplied by the reference, eg. 'Anonymous'." );
		$self->set_display( undef );
		$self->set_content( {
			allowed    => 1 ,
			suggestion => "Anonymous" ,
			value      => undef ,
		} );
		return $self;
	}

package Asame;
	use base qw/ _Model_Childless /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "ASAME" );
		$self->set_prompt( "Determines what text is used instead of the author list when multiple publications have the same authors, eg. an 'mdash'." );
		$self->set_help( "Determines what text is used instead of the author list when multiple publications have the same authors, eg. an 'mdash'." );
		$self->set_display( undef );
		$self->set_content( {
			allowed    => 1 ,
			suggestion => undef ,
			value      => undef ,
		} );
		return $self;
	}

package AuthorSeps;
	use base qw/ _Model_Order /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "AUTHORSEPS" );
		$self->set_prompt( "Container for elements which determine what punctuation occurs between authors." );
		$self->set_help( "Container for elements which determine what punctuation occurs between authors." );
		$self->set_display( undef );
		$self->set_order( [
			[ "TwoSeps"   , "1" ] ,
			[ "ThreeSeps" , "1" ] ,
		] );
		return $self;
	}

package TwoSeps;
	use base qw/ _Model_Childless /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "TWOSEPS" );
		$self->set_prompt( "Determines what character(s) to insert between authors when there are only two authors for a publication." );
		$self->set_help( "Determines what character(s) to insert between authors when there are only two authors for a publication." );
		$self->set_display( undef );
		$self->set_content( {
			allowed    => 1 ,
			suggestion => undef ,
			value      => undef ,
		} );
		return $self;
	}

package ThreeSeps;
	use base qw/ _Model_Order /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "THREESEPS" );
		$self->set_prompt( "Contains elements that determine what characters to insert between authors when there are more than two authors." );
		$self->set_help( "Contains elements that determine what characters to insert between authors when there are more than two authors." );
		$self->set_display( undef );
		$self->set_order( [
			[ "ThreeSepsEach" , "1" ] ,
			[ "ThreeSepsLast" , "1" ] ,
		] );
		return $self;
	}

package ThreeSepsEach;
	use base qw/ _Model_Childless /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "THREESEPSEACH" );
		$self->set_prompt( "When there are three or more authors, this element determines what character(s) to insert between all but the last pair of authors." );
		$self->set_help( "When there are three or more authors, this element determines what character(s) to insert between all but the last pair of authors." );
		$self->set_display( undef );
		$self->set_content( {
			allowed    => 1 ,
			suggestion => undef ,
			value      => undef ,
		} );
		return $self;
	}

package ThreeSepsLast;
	use base qw/ _Model_Childless /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "THREESEPSLAST" );
		$self->set_prompt( "When there are three or more authors, this element determines what character(s) to insert between the last pair of authors." );
		$self->set_help( "When there are three or more authors, this element determines what character(s) to insert between the last pair of authors." );
		$self->set_display( undef );
		$self->set_content( {
			allowed    => 1 ,
			suggestion => undef ,
			value      => undef ,
		} );
		return $self;
	}

package AuthorNames;
	use base qw/ _Model_Order /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "AUTHORNAMES" );
		$self->set_prompt( "Contains elements which determine the formatting of author names." );
		$self->set_help( "Contains elements which determine the formatting of author names." );
		$self->set_display( undef );
		$self->set_order( [
			[ "NameFirst" , "1" ] ,
			[ "NameOther" , "1" ] ,
		] );
		return $self;
	}

package NameFirst;
	use base qw/ _Model_Childless /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "NAMEFIRST" );
		$self->set_prompt( "Determines the formatting of the first author's name, including the ordering of sur- and forenames, punctuation of initials and whether any part of the name is in upper case." );
		$self->set_help( "Determines the formatting of the first author's name, including the ordering of sur- and forenames, punctuation of initials and whether any part of the name is in upper case." );
		$self->set_display( undef );
		$self->set_attributes( {
			mandatory => undef ,
			optional  => [ "InitialStyle" , "NameOrder" , "UpperCase" ] ,
		} );
		return $self;
	}

package NameOther;
	use base qw/ _Model_Childless /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "NAMEOTHER" );
		$self->set_prompt( "Contains elements which determine the formatting of second and subsequent authors' names, including such things as ordering of surn- and forenames, punctuation of initials and whether any part of the name is in upper case." );
		$self->set_help( "Contains elements which determine the formatting of second and subsequent authors' names, including such things as ordering of surn- and forenames, punctuation of initials and whether any part of the name is in upper case." );
		$self->set_display( undef );
		$self->set_attributes( {
			mandatory => undef ,
			optional  => [ "InitialStyle" , "NameOrder" , "UpperCase" ] ,
		} );
		return $self;
	}

package Text;
	use base qw/ _Model_Order /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "TEXT" );
		$self->set_prompt( "Contains elements which determine what text, if any, precedes and follows the author list." );
		$self->set_help( "Contains elements which determine what text, if any, precedes and follows the author list." );
		$self->set_display( undef );
		$self->set_order( [
			[ "TextSingle"   , "?" ] ,
			[ "TextMultiple" , "?" ] ,
		] );
		return $self;
	}

package TextSingle;
	use base qw/ _Model_Order /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "TEXTSINGLE" );
		$self->set_prompt( "Determines what text, if any, precedes and follows the author in a single author work." );
		$self->set_help( "Determines what text, if any, precedes and follows the author in a single author work." );
		$self->set_display( undef );
		$self->set_order( [
			[ "Preceeding" , "?" ] ,
			[ "Following"  , "?" ] ,
		] );
		return $self;
	}

package TextMultiple;
	use base qw/ _Model_Order /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "TEXTMULTIPLE" );
		$self->set_prompt( "Determines what text, if any, precedes and follows the author list in a multiple author work." );
		$self->set_help( "Determines what text, if any, precedes and follows the author list in a multiple author work." );
		$self->set_display( undef );
		$self->set_order( [
			[ "Preceeding" , "?" ] ,
			[ "Following"  , "?" ] ,
		] );
		return $self;
	}

package PubDate;
	use base qw/ _Model_Order /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "PUBDATE" );
		$self->set_prompt( "Specifies the location of a publication date [can be any of ris elements: PY|Y1, Y2] in the citation/reference.  Contains elements and attributes which determine how the publication date is formatted." );
		$self->set_help( "Specifies the location of a publication date [can be any of ris elements: PY|Y1, Y2] in the citation/reference.  Contains elements and attributes which determine how the publication date is formatted." );
		$self->set_display( "name" );
		$self->set_summary( "name" );
		$self->set_attributes( {
			mandatory => [ "Role_PubDate" ] ,
			optional  => [ "Sequence" , "Format" , "YearFormat" , "MonthFormat" , "PadLeadingZero" , "Style" , "DayFormat" ] ,
		} );
		$self->set_order( [
			[ "Preceeding" , "?" ] ,
			[ "Following"  , "?" ] ,
			[ "FirstSep"   , "?" ] ,
			[ "SecondSep"  , "?" ] ,
		] );
		return $self;
	}

package FirstSep;
	use base qw/ _Model_Childless /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "FIRSTSEP" );
		$self->set_prompt( "Determines what character(s) appear between the first and second parts of the publication date, eg. '/' between day and month." );
		$self->set_help( "Determines what character(s) appear between the first and second parts of the publication date, eg. '/' between day and month." );
		$self->set_display( undef );
		$self->set_content( {
			allowed    => 1 ,
			suggestion => undef ,
			value      => undef ,
		} );
		return $self;
	}

package SecondSep;
	use base qw/ _Model_Childless /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "SECONDSEP" );
		$self->set_prompt( "Determines what character(s) appear between the second and third parts of the publication date, eg. '/' between month and year." );
		$self->set_help( "Determines what character(s) appear between the second and third parts of the publication date, eg. '/' between month and year." );
		$self->set_display( undef );
		$self->set_content( {
			allowed    => 1 ,
			suggestion => undef ,
			value      => undef ,
		} );
		return $self;
	}

package Title;
	use base qw/ _Model_Order /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "TITLE" );
		$self->set_prompt( "Specifies the location of a title [can be any of ris elements: BT|TI, T2, T3] in the citation/reference.  Determines how the title is formatted." );
		$self->set_help( "Specifies the location of a title [can be any of ris elements: BT|TI, T2, T3] in the citation/reference.  Determines how the title is formatted." );
		$self->set_display( "name" );
		$self->set_summary( "name" );
		$self->set_attributes( {
			mandatory => [ "Role_Title" ] ,
			optional  => [ "Case" , "Style" ] ,
		} );
		$self->set_order( [
			[ "Preceeding" , "?" ] ,
			[ "Following"  , "?" ] ,
		] );
		return $self;
	}

package JournalName;
	use base qw/ _Model_Order /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "JOURNALNAME" );
		$self->set_prompt( "Specifies the location of journal name [can be any of ris elements: JO, JF, J1, J2] in the citation/reference.  Determines how the journal name is formatted." );
		$self->set_help( "Specifies the location of journal name [can be any of ris elements: JO, JF, J1, J2] in the citation/reference.  Determines how the journal name is formatted." );
		$self->set_display( "name" );
		$self->set_summary( "name" );
		$self->set_attributes( {
			mandatory => undef ,
			optional  => [ "AlternateText_JournalName" , "DefaultText" , "DefaultText" , "Punctuation" , "Case" , "Style" ] ,
		} );
		$self->set_order( [
			[ "Preceeding" , "?" ] ,
			[ "Following"  , "?" ] ,
		] );
		return $self;
	}

package Volume;
	use base qw/ _Model_Order /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "VOLUME" );
		$self->set_prompt( "Specifies the location of volume [ris element: VL] in the citation/reference.  Determines how the volume is formatted." );
		$self->set_help( "Specifies the location of volume [ris element: VL] in the citation/reference.  Determines how the volume is formatted." );
		$self->set_display( "name" );
		$self->set_summary( "name" );
		$self->set_attributes( {
			mandatory => undef ,
			optional  => [ "Style" ] ,
		} );
		$self->set_order( [
			[ "Preceeding" , "?" ] ,
			[ "Following"  , "?" ] ,
		] );
		return $self;
	}

package Issue;
	use base qw/ _Model_Order /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "ISSUE" );
		$self->set_prompt( "Specifies the location of issue [ris element: IS] in the citation/reference.  Determines how the issue is formatted." );
		$self->set_help( "Specifies the location of issue [ris element: IS] in the citation/reference.  Determines how the issue is formatted." );
		$self->set_display( "name" );
		$self->set_summary( "name" );
		$self->set_attributes( {
			mandatory => undef ,
			optional  => [ "Style" ] ,
		} );
		$self->set_order( [
			[ "Preceeding" , "?" ] ,
			[ "Following"  , "?" ] ,
		] );
		return $self;
	}

package Pages;
	use base qw/ _Model_Order /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "PAGES" );
		$self->set_prompt( "Specifies the location of the page reference [ris element(s): SP +/- EP] in the citation/reference.  Contains elements and attributes which determine how the page reference is formatted." );
		$self->set_help( "Specifies the location of the page reference [ris element(s): SP +/- EP] in the citation/reference.  Contains elements and attributes which determine how the page reference is formatted." );
		$self->set_display( "name" );
		$self->set_summary( "name" );
		$self->set_attributes( {
			mandatory => undef ,
			optional  => [ "Style" ] ,
		} );
		$self->set_order( [
			[ "Preceeding" , "?" ] ,
			[ "Following"  , "?" ] ,
			[ "SinglePage" , "?" ] ,
			[ "PageRange"  , "?" ] ,
		] );
		return $self;
	}

package SinglePage;
	use base qw/ _Model_Order /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "SINGLEPAGE" );
		$self->set_prompt( "Determines how a single-page page reference is formatted." );
		$self->set_help( "Determines how a single-page page reference is formatted." );
		$self->set_display( undef );
		$self->set_order( [
			[ "Preceeding" , "?" ] ,
			[ "Following"  , "?" ] ,
		] );
		return $self;
	}

package PageRange;
	use base qw/ _Model_Order /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "PAGERANGE" );
		$self->set_prompt( "Determines how a multiple-page page reference is formatted." );
		$self->set_help( "Determines how a multiple-page page reference is formatted." );
		$self->set_display( undef );
		$self->set_attributes( {
			mandatory => undef ,
			optional  => [ "Type_PageRange" ] ,
		} );
		$self->set_order( [
			[ "Preceeding"     , "?" ] ,
			[ "RangeSeparator" , "?" ] ,
			[ "Following"      , "?" ] ,
		] );
		return $self;
	}

package RangeSeparator;
	use base qw/ _Model_Childless /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "RANGESEPARATOR" );
		$self->set_prompt( "Determines what character(s) to use to separate start and end page numbers in a mult-page page reference, eg. a '-'." );
		$self->set_help( "Determines what character(s) to use to separate start and end page numbers in a mult-page page reference, eg. a '-'." );
		$self->set_display( undef );
		$self->set_content( {
			allowed    => 1 ,
			suggestion => "-" ,
			value      => undef ,
		} );
		return $self;
	}

package Publisher;
	use base qw/ _Model_Order /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "PUBLISHER" );
		$self->set_prompt( "Specifies the location of the publisher [ris element: PB] in the citation/reference.  Determines how the publisher is formatted." );
		$self->set_help( "Specifies the location of the publisher [ris element: PB] in the citation/reference.  Determines how the publisher is formatted." );
		$self->set_display( "name" );
		$self->set_summary( "name" );
		$self->set_attributes( {
			mandatory => undef ,
			optional  => [ "Style" ] ,
		} );
		$self->set_order( [
			[ "Preceeding" , "?" ] ,
			[ "Following"  , "?" ] ,
		] );
		return $self;
	}

package PubPlace;
	use base qw/ _Model_Order /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "PUBPLACE" );
		$self->set_prompt( "Specifies the location of the city of publication [ris element: CY] in the citation/reference.  Determines how the city of publication is formatted." );
		$self->set_help( "Specifies the location of the city of publication [ris element: CY] in the citation/reference.  Determines how the city of publication is formatted." );
		$self->set_display( "name" );
		$self->set_summary( "name" );
		$self->set_attributes( {
			mandatory => undef ,
			optional  => [ "Style" ] ,
		} );
		$self->set_order( [
			[ "Preceeding" , "?" ] ,
			[ "Following"  , "?" ] ,
		] );
		return $self;
	}

package Serial;
	use base qw/ _Model_Order /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "SERIAL" );
		$self->set_prompt( "Specifies the location of serial number (usually ISSN or ISBN number) [ris element: SN] in the reference.  Determines how the serial number is formatted." );
		$self->set_help( "Specifies the location of serial number (usually ISSN or ISBN number) [ris element: SN] in the reference.  Determines how the serial number is formatted." );
		$self->set_display( "name" );
		$self->set_summary( "name" );
		$self->set_attributes( {
			mandatory => undef ,
			optional  => [ "Style" ] ,
		} );
		$self->set_order( [
			[ "Preceeding" , "?" ] ,
			[ "Following"  , "?" ] ,
		] );
		return $self;
	}

package Address;
	use base qw/ _Model_Order /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "ADDRESS" );
		$self->set_prompt( "Specifies the location of the corresponding author's contact details [ris element: AD] in the citation/reference.  Determines how the contact details are formatted." );
		$self->set_help( "Specifies the location of the corresponding author's contact details [ris element: AD] in the citation/reference.  Determines how the contact details are formatted." );
		$self->set_display( "name" );
		$self->set_summary( "name" );
		$self->set_attributes( {
			mandatory => undef ,
			optional  => [ "Style" ] ,
		} );
		$self->set_order( [
			[ "Preceeding" , "?" ] ,
			[ "Following"  , "?" ] ,
		] );
		return $self;
	}

package UserDef;
	use base qw/ _Model_Order /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "USERDEF" );
		$self->set_prompt( "Specifies the location of a user-defined field [can be any of ris elements: U1, U2, U3, U4, U5] in the citation/reference.  Determines how the field's contents are formatted." );
		$self->set_help( "Specifies the location of a user-defined field [can be any of ris elements: U1, U2, U3, U4, U5] in the citation/reference.  Determines how the field's contents are formatted." );
		$self->set_display( "name" );
		$self->set_summary( "name" );
		$self->set_attributes( {
			mandatory => [ "Role_UserDef" ] ,
			optional  => [ "Style" ] ,
		} );
		$self->set_order( [
			[ "Preceeding" , "?" ] ,
			[ "Following"  , "?" ] ,
		] );
		return $self;
	}

	# Class methods
	sub default_copyable { undef; }

	sub _test_attributes_mandatory {  # override to prevent Role_UserDef dialog
		return Role_UserDef->get_enumeration();
	}

package Misc;
	use base qw/ _Model_Order /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "MISC" );
		$self->set_prompt( "Specifies the location of a miscellaneous field [can be any of ris elements: M1, M2, M3] in the citation/reference.  Determines how the field's contents are formatted." );
		$self->set_help( "Specifies the location of a miscellaneous field [can be any of ris elements: M1, M2, M3] in the citation/reference.  Determines how the field's contents are formatted." );
		$self->set_display( "name" );
		$self->set_summary( "name" );
		$self->set_attributes( {
			mandatory => [ "Role_Misc" ] ,
			optional  => [ "Style" ] ,
		} );
		$self->set_order( [
			[ "Preceeding" , "?" ] ,
			[ "Following"  , "?" ] ,
		] );
		return $self;
	}

	# Class methods
	sub default_copyable { undef; }

	sub _test_attributes_mandatory {  # override to prevent Role_Misc dialog
		return Role_Misc->get_enumeration();
	}

package Notes;
	use base qw/ _Model_Order /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "NOTES" );
		$self->set_prompt( "Specifies the location of notes [ris element: N1] in the citation/reference.  Determines how the notes are formatted." );
		$self->set_help( "Specifies the location of notes [ris element: N1] in the citation/reference.  Determines how the notes are formatted." );
		$self->set_display( "name" );
		$self->set_summary( "name" );
		$self->set_attributes( {
			mandatory => undef ,
			optional  => [ "Style" ] ,
		} );
		$self->set_order( [
			[ "Preceeding" , "?" ] ,
			[ "Following"  , "?" ] ,
		] );
		return $self;
	}

package Abstract;
	use base qw/ _Model_Order /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "ABSTRACT" );
		$self->set_prompt( "Specifies the location of the abstract [ris element: AB|N2] in the citation/reference.  Determines how the abstract is formatted." );
		$self->set_help( "Specifies the location of the abstract [ris element: AB|N2] in the citation/reference.  Determines how the abstract is formatted." );
		$self->set_display( "name" );
		$self->set_summary( "name" );
		$self->set_attributes( {
			mandatory => undef ,
			optional  => [ "Style" ] ,
		} );
		$self->set_order( [
			[ "Preceeding" , "?" ] ,
			[ "Following"  , "?" ] ,
		] );
		return $self;
	}

package Link;
	use base qw/ _Model_Order /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "LINK" );
		$self->set_prompt( "Specifies the location of a UR field or a link field [L1 - L4].  Determines how the field's contents are formatted." );
		$self->set_help( "Specifies the location of a link field [can be any of UR, L1, L2, L3, L4] in the citation reference.  Determines how the field's contents are formatted." );
		$self->set_display( "name" );
		$self->set_summary( "name" );
		$self->set_attributes( {
			mandatory => [ "Role_Link" ] ,
			optional  => [ "Style" ] ,
		} );
		$self->set_order( [
			[ "Preceeding" , "?" ] ,
			[ "Following"  , "?" ] ,
		] );
		return $self;
	}

	# Class methods
	sub default_copyable { undef; }

	sub _test_attributes_mandatory {  # override to prevent Role_Link dialog
		return Role_Link->get_enumeration();
	}

package Separator;
	use base qw/ _Model_Childless /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "SEPARATOR" );
		$self->set_prompt( "Specifies the location of a pre-defined character sequence in the citation/reference.  Determines what this character sequence is, eg. '. '." );
		$self->set_help( "Specifies the location of a pre-defined character sequence in the citation/reference.  Determines what this character sequence is, eg. '. '." );
		$self->set_display( "content" );
		$self->set_summary( "content" );
		$self->set_content( {
			allowed    => 1 ,
			suggestion => undef ,
			value      => undef ,
		} );
		return $self;
	}

package CitStyle;
	use base qw/ _Model_Order /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "CITSTYLE" );
		$self->set_prompt( "Contains elements and attributes which determine the formatting of citations." );
		$self->set_help( "Contains elements and attributes which determine the formatting of citations." );
		$self->set_display( "name" );
		$self->set_attributes( {
			mandatory => undef ,
			optional  => [ "InTextSequence" , "Style" ] ,
		} );
		$self->set_order( [
			[ "Preceeding"     , "?" ] ,
			[ "Following"      , "?" ] ,
			[ "CitSeparator"   , "1" ] ,
			[ "RangeSeparator" , "?" ] ,
			[ "InTextDef"      , "?" ] ,
			[ "AuthorOnly"     , "?" ] ,
			[ "YearOnly"       , "?" ] ,
		] );
		return $self;
	}

	# Class methods
	sub default_copyable { undef; }

package CitSeparator;
	use base qw/ _Model_Childless /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "CITSEPARATOR" );
		$self->set_prompt( "Determines what character(s) separates individual references in a multi-reference citation." );
		$self->set_help( "Determines what character(s) separates individual references in a multi-reference citation." );
		$self->set_display( undef );
		$self->set_content( {
			allowed    => 1 ,
			suggestion => ", " ,
			value      => undef ,
		} );
		return $self;
	}

	# Class methods
	sub default_copyable { undef; }

package BibStyle;
	use base qw/ _Model_Order /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "BIBSTYLE" );
		$self->set_prompt( "Contains elements and attributes which determine the formatting of bibliographies/reference lists." );
		$self->set_help( "Contains elements and attributes which determine the formatting of bibliographies/reference lists." );
		$self->set_display( "name" );
		$self->set_attributes( {
			mandatory => undef ,
			optional  => [ "BiblioSequence" , "BiblioFirstIndent" , "BiblioBlockIndent" , "FontSize" ] ,
		} );
		$self->set_order( [
			[ "BiblioTitle"    , "?" ] ,
			[ "Months"         , "?" ] ,
		] );
		return $self;
	}

	# Class methods
	sub default_copyable { undef; }

package BiblioTitle;
	use base qw/ _Model_Childless /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "BIBLIOTITLE" );
		$self->set_prompt( "Specifies the heading of the bibliography.  Examples include 'Bibliography' and 'Reference List'." );
		$self->set_help( "Specifies the heading of the bibliography.  Examples include 'Bibliography' and 'Reference List'." );
		$self->set_display( "name" );
		$self->set_content( {
			allowed    => 1 ,
			suggestion => "Bibliography" ,
			value      => undef ,
		} );
		return $self;
	}

	# Class methods
	sub default_copyable { undef; }

package InTextDef;
	use base qw/ _Model_Noorder /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "INTEXTDEF" );
		$self->set_prompt( "Contains elements which determine the formatting of 'regular', ie. author-year, citations (generated by <citation role=\"REFDB\">citekey</citation>)." );
		$self->set_help( "Contains elements which determine the formatting of 'regular', ie. author-year, citations (generated by <citation role=\"REFDB\">citekey</citation>)." );
		$self->set_display( "name" );
		#$self->set_summary( "name" );
		$self->reset_all_counters();
		return $self;
	}

	# Class methods
	sub default_copyable { undef; }

package AuthorOnly;
	use base qw/ _Model_Noorder /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "AUTHORONLY" );
		$self->set_prompt( "Contains elements which determine the formatting of author-only citations (generated by <citation role=\"REFDB\">A:citekey</citation>)." );
		$self->set_help( "Contains elements which determine the formatting of author-only citations (generated by <citation role=\"REFDB\">A:citekey</citation>)." );
		$self->set_display( "name" );
		#$self->set_summary( "name" );
		$self->reset_all_counters();
		return $self;
	}

	# Class methods
	sub default_copyable { undef; }

package YearOnly;
	use base qw/ _Model_Noorder /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "YEARONLY" );
		$self->set_prompt( "Contains elements which determine the formatting of year-only citations (generated by <citation role=\"REFDB\">Y:citekey</citation>)." );
		$self->set_help( "Contains elements which determine the formatting of year-only citations (generated by <citation role=\"REFDB\">Y:citekey</citation>)." );
		$self->set_display( "name" );
		#$self->set_summary( "name" );
		$self->reset_all_counters();
		return $self;
	}

	# Class methods
	sub default_copyable { undef; }

package Months;
	use base qw/ _Model_Order /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "MONTHS" );
		$self->set_prompt( "Contains elements determining the naming and abbreviation of months." );
		$self->set_help( "Contains elements determining the naming and abbreviation of months." );
		$self->set_display( "name" );
		$self->set_order( [
			[ "Jan" , "1" ] ,
			[ "Feb" , "1" ] ,
			[ "Mar" , "1" ] ,
			[ "Apr" , "1" ] ,
			[ "May" , "1" ] ,
			[ "Jun" , "1" ] ,
			[ "Jul" , "1" ] ,
			[ "Aug" , "1" ] ,
			[ "Sep" , "1" ] ,
			[ "Oct" , "1" ] ,
			[ "Nov" , "1" ] ,
			[ "Dec" , "1" ] ,
		] );
		return $self;
	}

	# Class methods
	sub default_copyable { undef; }

package Jan;
	use base qw/ _Model_Childless /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "JAN" );
		$self->set_prompt( "Determines the name and abbreviation of the first calendar month." );
		$self->set_help( "Determines the name and abbreviation of the first calendar month." );
		$self->set_display( undef );
		$self->set_attributes( {
			mandatory => [ "Threelet_Jan" , "Abbrev_Jan" , "Full_Jan" ] ,
			optional  => undef ,
		} );
		return $self;
	}

	# Class methods
	sub default_copyable { undef; }

package Feb;
	use base qw/ _Model_Childless /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "FEB" );
		$self->set_prompt( "Determines the name and abbreviation of the second calendar month." );
		$self->set_help( "Determines the name and abbreviation of the second calendar month." );
		$self->set_display( undef );
		$self->set_attributes( {
			mandatory => [ "Threelet_Feb" , "Abbrev_Feb" , "Full_Feb" ] ,
			optional  => undef ,
		} );
		return $self;
	}

	# Class methods
	sub default_copyable { undef; }

package Mar;
	use base qw/ _Model_Childless /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "MAR" );
		$self->set_prompt( "Determines the name and abbreviation of the third calendar month." );
		$self->set_help( "Determines the name and abbreviation of the third calendar month." );
		$self->set_display( undef );
		$self->set_attributes( {
			mandatory => [ "Threelet_Mar" , "Abbrev_Mar" , "Full_Mar" ] ,
			optional  => undef ,
		} );
		return $self;
	}

	# Class methods
	sub default_copyable { undef; }

package Apr;
	use base qw/ _Model_Childless /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "APR" );
		$self->set_prompt( "Determines the name and abbreviation of the fourth calendar month." );
		$self->set_help( "Determines the name and abbreviation of the fourth calendar month." );
		$self->set_display( undef );
		$self->set_attributes( {
			mandatory => [ "Threelet_Apr" , "Abbrev_Apr" , "Full_Apr" ] ,
			optional  => undef ,
		} );
		return $self;
	}

	# Class methods
	sub default_copyable { undef; }

package May;
	use base qw/ _Model_Childless /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "MAY" );
		$self->set_prompt( "Determines the name and abbreviation of the fifth calendar month." );
		$self->set_help( "Determines the name and abbreviation of the fifth calendar month." );
		$self->set_display( undef );
		$self->set_attributes( {
			mandatory => [ "Threelet_May" , "Abbrev_May" , "Full_May" ] ,
			optional  => undef ,
		} );
		return $self;
	}

	# Class methods
	sub default_copyable { undef; }

package Jun;
	use base qw/ _Model_Childless /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "JUN" );
		$self->set_prompt( "Determines the name and abbreviation of the sixth calendar month." );
		$self->set_help( "Determines the name and abbreviation of the sixth calendar month." );
		$self->set_display( undef );
		$self->set_attributes( {
			mandatory => [ "Threelet_Jun" , "Abbrev_Jun" , "Full_Jun" ] ,
			optional  => undef ,
		} );
		return $self;
	}

	# Class methods
	sub default_copyable { undef; }

package Jul;
	use base qw/ _Model_Childless /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "JUL" );
		$self->set_prompt( "Determines the name and abbreviation of the seventh calendar month." );
		$self->set_help( "Determines the name and abbreviation of the seventh calendar month." );
		$self->set_display( undef );
		$self->set_attributes( {
			mandatory => [ "Threelet_Jul" , "Abbrev_Jul" , "Full_Jul" ] ,
			optional  => undef ,
		} );
		return $self;
	}

	# Class methods
	sub default_copyable { undef; }

package Aug;
	use base qw/ _Model_Childless /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "AUG" );
		$self->set_prompt( "Determines the name and abbreviation of the eighth calendar month." );
		$self->set_help( "Determines the name and abbreviation of the eighth calendar month." );
		$self->set_display( undef );
		$self->set_attributes( {
			mandatory => [ "Threelet_Aug" , "Abbrev_Aug" , "Full_Aug" ] ,
			optional  => undef ,
		} );
		return $self;
	}

	# Class methods
	sub default_copyable { undef; }

package Sep;
	use base qw/ _Model_Childless /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "SEP" );
		$self->set_prompt( "Determines the name and abbreviation of the ninth calendar month." );
		$self->set_help( "Determines the name and abbreviation of the ninth calendar month." );
		$self->set_display( undef );
		$self->set_attributes( {
			mandatory => [ "Threelet_Sep" , "Abbrev_Sep" , "Full_Sep" ] ,
			optional  => undef ,
		} );
		return $self;
	}

	# Class methods
	sub default_copyable { undef; }

package Oct;
	use base qw/ _Model_Childless /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "OCT" );
		$self->set_prompt( "Determines the name and abbreviation of the tenth calendar month." );
		$self->set_help( "Determines the name and abbreviation of the tenth calendar month." );
		$self->set_display( undef );
		$self->set_attributes( {
			mandatory => [ "Threelet_Oct" , "Abbrev_Oct" , "Full_Oct" ] ,
			optional  => undef ,
		} );
		return $self;
	}

	# Class methods
	sub default_copyable { undef; }

package Nov;
	use base qw/ _Model_Childless /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "NOV" );
		$self->set_prompt( "Determines the name and abbreviation of the eleventh calendar month." );
		$self->set_help( "Determines the name and abbreviation of the eleventh calendar month." );
		$self->set_display( undef );
		$self->set_attributes( {
			mandatory => [ "Threelet_Nov" , "Abbrev_Nov" , "Full_Nov" ] ,
			optional  => undef ,
		} );
		return $self;
	}

	# Class methods
	sub default_copyable { undef; }

package Dec;
	use base qw/ _Model_Childless /;

	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);
		$self->set_name( "DEC" );
		$self->set_prompt( "Determines the name and abbreviation of the twelfth calendar month" );
		$self->set_help( "Determines the name and abbreviation of the twelfth calendar month" );
		$self->set_display( undef );
		$self->set_attributes( {
			mandatory => [ "Threelet_Dec" , "Abbrev_Dec" , "Full_Dec" ] ,
			optional  => undef ,
		} );
		return $self;
	}

	# Class methods
	sub default_copyable { undef; }

=head1 USER-INTERFACE CLASS

This class holds the user-built bibliography style.

=cut

package UI;
	use base qw/ _Base /;

=head2 UI: Public Interface

=head3 UI: Data members

	%self = (

		help  =>  {
			
			<help topic>       =>  <help text> ,

			...

		} ,

		cache  =>  {

			"element_class_A"  =>  element_A ,

			"element_class_B"  =>  element_B ,

			...

		} ,

		root  =>  "undef" | element ,
		
		filename  =>  undef | <filename>,
		
	)

=over

=cut

	# Constructor
	sub new {
		my $proto = shift;
		my $class = ref($proto) || $proto;
		my $self = $class->SUPER::new(@_);

=item

B<help>

Introductory help system.

Is a hash consisting of E<lt>"help topic" : "help text"E<gt> pairs.

=cut

		$self->{help}       = $self->default_help();

=item

B<cache>

Cache for frequently used elements.

First hash key ("cachable") points to list of cachable elements.

Second has key points to subsidiary hash which holds key:value pairs of element class names and the corresponding element.

=cut

		$self->{cache}      = $self->default_cache();

=item

B<root>

Location of style's root element.

=cut

		$self->{root}      = $self->default_root();

=item

B<style_file>

Name of style file.  Set after successful output.

=cut

		$self->{style_file}      = $self->default_style_file();

=item

B<summary_file>

Name of summary file.  Set after successful output.

=back

=cut

		$self->{summary_file}      = $self->default_summary_file();

=head3 UI: Constructors

	my $ui = UI->new();

=cut

		return $self;
	}

	# Default values
	sub default_help { 
		my $self = shift;
		my $cache = {};
		$cache->{"0. README"} = 
			"HELP TOPIC: README\n\nThis help consists of five topics (including this one):\n\n[RefDB]\nWhat RefDB is.\n\n[XML]\nThe format in which styles are specified.  A (very) brief introduction to XML.\n\n[Styles]\nA brief summary of style structure and hints on how to write styles.  You really should read this help topic.\n\n[Using this utility]\nSome notes on how to use this utility.\n\nThe help topics are designed to be read in the order they are listed above.  The last two, in particular, should be read by first-time users of this utility.\n\nThis utility has a number of limitations regarding style file generation.  Chief among them is that it cannot be used to edit an existing style.  This tool is designed for the initial \"heavy lifting\" of generating the bulk of the style definition.  Unless and until someone designs a utility to edit existing styles, any further refinement must be done \"by hand\".  Remember, a style file is an xml file following the citestylex DTD.  There are a number of tools which simplify the task of editing xml documents.\n\nIf you discover any bugs in this utility, please spend a few minutes to notify the refdb-users newsgroup (subscribe at <lists.sourceforge.net/lists/listinfo/refdb-users>).  Session transcripts (use the 'script' utility, try 'man script' for details) and any xml output will greatly aid in troubleshooting problems.\n\nThis utility is released under the same copyright and use conditions as the RefDB project <refdb.sourceforge.net>.";
		$cache->{"1. RefDB"} =
			"HELP TOPIC: RefDB\n\nRefDB is a bibliography tool.\n\nRefDB's homepage (<http://refdb.sourceforge.net>) provides this summary:\n\n\"RefDB is a reference database and bibliography tool for SGML, XML, and LaTeX/BibTeX documents. It allows users to share databases over a network. It is lightweight and portable to basically all platforms with a decent C compiler. And it's released under the GNU General Public License.\"\n\nOne use for RefDB is formatting document bibliographies and citations.  Such formatting includes:\n  - which bibliographic elements -- such as volume, issue and title -- to include,\n  - the order of bibliographic elements\n  - appearance of bibliographic elements (eg. italicised, bolded, uppercase, etc.), and\n  - punctuation between bibliographic elements.\n\nThe rules RefDB uses to format bibliographies and citations are specified in style files (see help topic <Styles>).\n\nThese style files are valid xml documents (see help topic <XML>).";
		$cache->{"2. XML"} = 
			"HELP TOPIC: XML\n\nRefDB style files are well-formed XML files which conform to the \"citestylex\" dtd.  While this utility makes generating style files easier than writing them \"by hand\", you should still have some understanding of xml before using it.  This is the briefest of introductions to the topic.  Several concepts are greatly simplified.\n\nXML is the \"eXtensible Markup Language\".  For the purposes of RefDB stylesheets, the central feature of XML is \"elements\".  An element is a unit of information.  It has the following features: a name, opening and closing tags, attributes and content.  Here is an example element:\n\n<ABBREVIATEFIRST DISPLAYAUTHOR=\"255\" MAXAUTHOR=\"256\" STYLE=\"ITALIC\">et al.</ABBREVIATEFIRST>\n\nName:\nThe element's name is ABBREVIATEFIRST.\n\nTags:\nThe element is bounded by opening and closing tags which consist primarily of the element name enclosed in angle brackets.  The opening tag has the form '<ELEMENT>'.  The closing tag has the form '</ELEMENT>' -- note the slash preceding the element name.\n\nAttributes:\nAn element can have associated attributes.  Attributes have names and values.  In the example element above there are three attributes: DISPLAYAUTHOR, MAXAUTHOR and STYLE.  Attributes are placed inside an element's opening tag after the element name.  They are separated by spaces.  Most attributes have a list of possible legal values (an \"enumeration\").  Some can accept any value.\n\nContent:\nAn element's content appears between the opening and closing tags.  There are few restrictions on element content.\n\n\nFinally, a note about DTDs.  In order for XML documents to be useful there must be agreement about the meanings elements and attributes have.  One means of specifying these meanings is a Document Type Definition (DTD).  A DTD is a set of rules about what element names are legal and in what sequence they can occur, what attributes are associated with each element and what attribute values are legal.  The \"citestylex\" DTD determines the structure of RefDB styles.  This DTD is included with the RefDB installation.";
		$cache->{"3. Styles"} = 
			"HELP TOPIC: Styles\n\nAs mentioned in help topic <RefDB> \"styles\" provide rules for RefDB to follow when formatting bibliographies and citations.  Each style is defined in an XML document that conforms to the \"citestylex\" DTD (see help topic <XML> for the briefest of introductions to XML).  It will be most advantageous to have some understanding of XML before attempting to use this utility.\n\nEach style file can be divided broadly into four sections.\n\nFIRST SECTION (STYLENAME element)\nThe first section specifies the xml version, the controlling DTD and the style name.  It includes the STYLENAME element.\n\nSECOND SECTION (REFSTYLE and PUBSTYLE elements)\nThe second section contains formatting rules for bibliography references.  The REFSTYLE element includes a number of PUBSTYLE elements.  Each PUBSTYLE element contains a number of subsidiary elements.  Each PUBSTYLE element and its children specify formatting rules for a particular type of publication.  There are 35 possible publication types, including book, journal and electronic citation.  The publication type each PUBTYPE applies to is determined by PUBTYPE's TYPE attribute.\n\nThere are two kinds of elements in each PUBTYPE.  One type specifies a component of the reference, for example, the TITLE element signifies the title of each reference work and the AUTHORLIST signifies the list of authors of a given work.  These elements have a one-to-one correspondence with reference record ris fields.  For example, the VOLUME element corresponds to the ris VL field.  (See help topic <Using this utility> for how to access this information while creating your style.)  Their presence in the PUBTYPE signifies the presence of the associated component in the bibliography reference.  Also, their location in the PUBTYPE signifies the location of the associated component in the bibliography reference.  For example, if you put AUTHORLIST first in a PUBTYPE, then the authors will appear first in the reference.  If you put REFNUMBER first, the reference will begin with a reference number.\n\nSome elements are simple -- VOLUME, for example, has only one attribute and two possible child elements.  Other elements are complex -- AUTHORLIST, for example, has numerous possible child elements with many associated attributes.  See help topic <Using this utility> for notes on the help available while using this utility.\n\nThe second type of element specifies formatting and punctuation.  These elements include FOLLOWING, PRECEDING and SEPARATOR.  FOLLOWING and PRECEDING are associated with parent elements.  They appear in a reference only if their parent element does.  For example, you may specify the VOLUME element with a PRECEDING element containing 'Vol. '.  If a particular reference of that type contains a volume, for example '10', it will appear in the bibliography as 'Vol. 10'.  If a particular reference of that type does not contain a volume, the text 'Vol. ' will not appear in the bibliography reference.  SEPARATOR, on the other hand, is not associated with any parent elements and will always appear in the reference.  It should be used carefully, especially if nearby reference components may be missing from some references.\n\nSome elements can occur an unlimited number of times in each PUBTYPE, for example, PRECEDING, FOLLOWING and even VOLUME and ISSUE.  Others can occur a limited number of times: AUTHORLIST, for example, can occur no more than three times in a publication type, PUBDATE (publication date) no more than twice and TITLE no more than three times.\n\nAlthough there are 35 possible publication types you do not have to create a PUBTYPE for all of them.  As mentioned above, if a publication type contains an element not found in a particular reference, that element (and associated elements) are simply \"dropped\" from the bibliography for that particular reference.  PUBTYPE 'GEN' is the default publication type.  You should create it first and then create PUBTYPES for any publication types whose requirements are not met by the 'GEN' PUBTYPE.  For example, the 'GEN' PUBTYPE can be designed to meet the needs of an edited work -- it could contain two AUTHORLISTs and TITLEs, the first for the primary work and its authors and the second for the collection details and editors.  It will then also serve the needs of books -- the secondary title and author/editor list will simply be left blank.\n\nWarning: Be careful assigning PRECEDING and FOLLOWING child elements to adjacent elements or you may end up with double punctuation in your references.\n\nTHIRD SECTION (CITSTYLE, INTEXTDEF, AUTHORONLY and YEARONLY elements)\nThese elements determine the formatting of in-text citations.  RefDB provides for three types of citations: default (i.e., author-year), author-only and year only.  The formatting of these citation types are handled, respectively, by the INTEXTDEF, AUTHORONLY and YEARONLY elements (and associated attributes and children elements).  These elements follow a similar schema to that outlined for PUBTYPES: some elements specify the presence and order of citation components while other elements (and attributes) determine citation style.\n\nFOURTH SECTION (BIBSTYLE and BIBLIOTITLE elements)\nThe attributes associated with the BIBSTYLE element determine styling that affects the bibliography as a whole.  The BIBLIOTITLE element determines the title of the bibliography/reference list.\n\n\nA final note:\nThe STYLESET element is not available for selection using this utility.  Trust me, you won't need it.";
		$cache->{"4. Using this utility"} = 
			"HELP TOPIC: Using this utility\n\nIn short, this utility consists of a loop in which you are asked to:\n\n  - select an element from a list of elements allowed in that part of the style,\n  - enter or select appropriate values for mandatory attributes,\n  - select optional attributes for that element and enter their values,\n  - select an element from a list of elements ...\n\nThis process ends when you have exhausted the DTD (see section <XML>).\n\nEvery time you are presented with a list of elements you have the option of selecting help.  Doing so will present a screen with a summary of each element on the list.  The same thing happens when you are presented with a list of attributes.\n\nWhen an attribute or element is required by the DTD this utility will select it automatically.  For some objects you will not need to provide input and so this utility will create the object(s) automatically.  This can be disconcerting at first.  Please read the console feedback carefully.\n\nWhenever you are asked to enter or select a value for an attribute or element, you are given a brief prompt explaining the purpose of the element or attribute.\n\nAfter each element is added to the style you are presented with a \"progress report\" showing the (major) elements.  It may take a little while to understand this feedback.  As you add elements they will be appended to the report.  When you complete a major element, such as a PUBTYPE or AUTHORLIST, however, its subsidiary elements are no longer displayed -- they are \"folded\".  This saves space and makes it easier to quickly understand where in the style you are at that point in time.  As you become more familiar with the structure of the style DTD this display will become increasingly useful.\n\nAt certain times within PUBTYPE, AUTHORONLY, YEARONLY and INTEXTDEF elements you are able to delete the previous \"major\" element.  \"Major\" elements are those corresponding to ris fields.  These elements may contain other elements.  For example, deleting an AUTHORLIST element will delete all the children elements of that author list style.  You can view the complete element before deletion occurs.\n\nWhen you select some elements (in general, those corresponding to ris fields) you will be given an opportunity to copy the most recently entered element of that type.  If you choose to copy, all attributes and sub-elements will be copied.  This can save you a lot of time if, for example, your author lists will always have the same formatting.\n\nAfter you have created your style this utility will save it to a disk file.  It will also generate a brief summary of it in html format.  This summary can be of great help when entering references in your reference database.\n\nDepending on the version of this utility and the configuration of your system, this utility may offer to upload the style to RefDB.  There is no foreseeable way in which this operation could damage your reference data, but you use it at your own risk...";
		return $cache;
	}

	sub default_cache { 
		my $self = shift;
		my @cachable_elements = ( 
			"RefNumber" , 
			"AuthorList" , 
			"PubDate" , 
			"Title" , 
			"JournalName" , 
			"Volume" , 
			"Issue" , 
			"Pages" , 
			"Publisher" , 
			"PubPlace" , 
			"Serial" , 
			"Address" , 
			"UserDef" , 
			"Abstract" , 
		);
	   	my $cache = {};
		foreach ( @cachable_elements ) {
			$cache->{$_} = undef;
		}
		return $cache;
	}

	sub default_root { undef; }

	sub default_style_file { undef; }

	sub default_summary_file { undef; }

=head3 UI: Setters

	$ui->set_root( <element> );
	$ui->set_style_file( "<filename>" );
	$ui->set_summary_file( "<filename>" );

=cut

	sub set_root { $_[0]->{root} = $_[1]; }
	sub set_style_file { $_[0]->{style_file} = $_[1]; }
	sub set_summary_file { $_[0]->{summary_file} = $_[1]; }

=head3 UI: Getters

	$ui->get_help_topics();          # list:   keys   ( $self->{help} )
	$ui->get_help( "<topic>" );      # scalar: value  ( $self->{help}->{<topic>} )
	                                 #         takes parameter: help topic
	$ui->get_cachable();             # list:   $self->{cache}->{cachable}
	$ui->get_cached();               # list:   defined( $self->{cache}->{elements} )
	$ui->get_cached_element( "<class>" );
	                                 # object: cached element
	$ui->get_root();                 # object: root element
	$ui->get_style_file();           # scalar: style file filename
	$ui->get_summary_file();         # scalar: summary file filename

=cut

	sub get_help_topics { return keys %{ $_[0]->{help} }; }
	sub get_help { return $_[0]->{help}->{$_[1]}; }
	sub get_cachable { return @{ $_[0]->{cache}->{cachable} }; }
	sub get_cached { return keys %{ $_[0]->{cache} }; }
	sub get_cached_element { return $_[0]->{cache}->{$_[1]}; }
	sub get_root { return $_[0]->{root}; }
	sub get_style_file { return $_[0]->{style_file}; }
	sub get_summary_file { return $_[0]->{summary_file}; }

=head3 UI: Other methods

=over

=item

B<$ui-E<gt>help_system();>

Runs help system -- a menu allowing the selection of a series of 

=cut

	sub help_system { 
		my ( $self , $choice ) = ( shift , undef );
		my @topics = sort $self->get_help_topics();
		print "This is the help system.\n";
		push @topics , "[EXIT]";
		while ( 1 ) {
			while ( 1 ) {
				$choice = $self->_input_choice( "Select help topic:" , @topics );
				if ( defined( $choice ) ) { last; }
				print "Invalid choice.  Sorry, please try again.\n";
			}
			if ( $choice eq "[EXIT]" ) { last; }
			$self->_view( "" , $self->get_help( $choice ) );
		}
		print "Exiting help system.\n";
	}

=item

B<$ui-E<gt>get_last_parent();>

Returns reference to last parent element.

=cut

	sub get_last_parent { 
		my ( $self , $choice ) = ( shift , undef );
		my @topics = sort $self->get_help_topics();
		print "This is the help system.\n";
		push @topics , "[EXIT]";
		while ( 1 ) {
			while ( 1 ) {
				$choice = $self->_input_choice( "Select help topic:" , @topics );
				if ( defined( $choice ) ) { last; }
				print "Invalid choice.  Sorry, please try again.\n";
			}
			if ( $choice eq "[EXIT]" ) { last; }
			$self->_view( "" , $self->get_help( $choice ) );
		}
		print "Exiting help system.\n";
	}

=item

B<$ui-E<gt>can_copy_recent();>

Returns booelan indicating whether element can copy recent version of itself.

Parameters: [ 0 = class ] , 1 = element class | object

=cut

	sub can_copy_recent {
		my ( $self , $element ) = ( shift , shift );
		$element = ref( $element ) ? $element->get_classname() : $element;		
		my @allowable = ( 
			"RefNumber" , 
			"AuthorList" , 
			"PubDate" , 
			"Title" , 
			"JournalName" , 
			"Volume" , 
			"Issue" , 
			"Pages" , 
			"Publisher" , 
			"PubPlace" , 
			"Serial" , 
			"Address" , 
			"UserDef" , 
			"Abstract" , 
		);
		return grep /\b$element\b/ , @allowable;
	}

=item

B<$el-E<gt>delete_selected_element();>

Gives the user the option of deleting element(s).

Depending on the state of the element tree, the user can delete the last element or the last major ("named") element.  A "named" element has its 'display' data member set to 'name' -- in general they are the elements corresponding to ris fields.

A "complete" element has had all children added and its 'children-E<gt>complete' data member is set to true ("1").

Algorithm is:

	get last named element
	if element is complete
		repeat until last named element is deleted
			delete last element
		endrepeat
	else
		delete last element only
	endif

=cut

	sub delete_selected_element {
		my ( $self , $xml ) = ( shift );
		my $last_named = $self->get_root()->get_last_named();
		my @xml = $last_named->generate_full_xml();
		my $prompt = sprintf( "\nDelete back to (and including) %s?" , 
		                      $last_named->get_name() );
		my @options = qw/ Yes No View /;
		my $choice = undef;
		while ( 1 ) {
			$choice = $self->_input_choice( $prompt , @options );
			# switch: $choice
			# case: 'View'
			( $choice eq 'View' ) and do {
				$self->_view( "Element " . $last_named->get_name() , $xml[1] );
				next;
			};
			# case: 'Yes'
			( $choice eq 'Yes' ) and do {
				my ( $report , @names ) = ( "\nDeleted: " , () );
				while ( 1 ) {
					my $deleted = $self->get_root()->delete_last_element();
					push @names , $deleted->get_name();
					last if $deleted->get_classname() eq $last_named->get_classname();
				}
				$report .= shift @names;
				$report .= sprintf( ", %s" , shift @names ) while @names;
				$report .= ".\n";
				$self->_display( $report );
				return;
			};
			# case: 'No'
			( $choice eq 'No' ) and do {
				return;
			};
			# case: default
			( not grep $choice eq $_ , @options ) and do {
				die "No matching case block for \"$choice\"";
			};
			# endswitch
		}
	}

=item

B<$ui-E<gt>create_element();>

Creates element.  If element of class already exists, user can duplicate most recent.

=cut

	sub create_element {
		my ( $self , $class , $element ) = ( shift , shift );
		$element = $self->get_root()->get_most_recent( $class );
		if ( $element && 
		    	$self->can_copy_recent( $element ) && 
				$element->is_duplicatable() ) {
			my @xml = $element->generate_full_xml();
			my $prompt = sprintf "\nElement %s previously used.  Copy most recent?" , $element->get_name();
			my @options = qw/ Yes No View /;
			my $choice = undef;
			while ( 1 ) {
				$choice = $self->_input_choice( $prompt , @options );
				# switch: $choice
				# case: 'View'
				( $choice eq 'View' ) and do {
					$self->_view( "Most recent " . $element->get_name() , $xml[1] );
					next;
				};
				# case: 'Yes'
				( $choice eq 'Yes' ) and do {
					my $duplicate = $element->duplicate();
					$duplicate->set_complete( 1 );
					return $duplicate;
				};
				# case: 'No'
				( $choice eq 'No' ) and do {
					last;
				};
				# case: default
				( not grep $choice eq $_ , @options ) and do {
					die "No matching case block for \"$choice\"";
				};
				# endswitch
			}
		}
		$element = $class->new();
		$element->add_attributes();
		$element->enter_value();
		return $element;
	}

=item

B<$ui-E<gt>startup_checks();>

Currently checks for: write access to current directory.

=cut

	sub startup_checks {
		my ( $self , $return ) = ( shift , 1 );
		# Check for local disk read/write access
		# Failure = Fatal error
		eval {
			my ($fh, $filename) = File::Temp::tempfile( TEMPLATE => 'XXXXXXXX' ,
		                             DIR      => './' ,
								     UNLINK   => 1 ,
								   );
		};
		if ($@) {
			print "Unable to create a file in the current directory.\n";
			$return = 0;
		}
		return $return;		
	}

=item

B<$ui-E<gt>get_output_filename();>

Returns filename for output file.  Uses File::Temp module.

If filename does not exist in current directory, simply return default.

If it does, then user must select new filename.  File::Temp is used to provide a suggestion based on the default filename.

Requires the following parameters: [ 0 = class ] , 1 = default filename , 2 = filename template pattern , 3 = filename template suffix , 4 = prompt.

Parameters 2 and 3 are in the format required by File::Temp.  Parameter 2 must end in at leat 4 'X's -- these will be replaced with random digits.  The suffix is the part of the filename following the part specified by parameter 2.  It is not restricted to a DOS-style three letter extension.

Example: Default filename (parameter 1) = 'experimental-style.xml'.  Filename template pattern (parameter 2) = 'experimental-XXXX'.  Filename template suffix (parameter 3) = '-style.xml'.  Prompt (parameter 4) = 'Enter name for style file:'.

=cut

	sub get_output_filename {
		my ( $self , $default , $template , $suffix , $prompt ) = 
			( shift , shift , shift , shift , shift );
		my $filename = $default;
		if ( -e $filename ) {
			printf "\nWarning: Default filename <%s> already exists.\n", $filename;
			( undef , $filename) = File::Temp::tempfile( $template ,
														 SUFFIX => $suffix ,
														 UNLINK => 1 ,
									  				   );
			while ( 1 ) {
				$filename = $self->_input_ask( $prompt , $filename );
				last if ( ( $filename )
				       && ( not -e $filename )
					   && ( $filename =~ /^(\w|\.|\-)+$/ ) );  # no spaces
				print "Invalid filename.  Please try again.\n\n";
			}
		}
		return $filename;
	}

=item

B<$ui-E<gt>write_style();>

Writes style to file.  Checks filename doesn't already exist.  If so, proposes alternative.

=cut

	sub write_style {
		my ( $self , $return ) = ( shift , 0 );
		print "\nOutputting style to file.\n";
		my $stylename = $self->get_root()->get_style_name();
		my $filename = $self->get_output_filename(
			sprintf( "%s-style.xml" , $stylename ) , 
			sprintf( "%s-style-XXXX" , $stylename ) , 
			".xml" ,
			"Enter filename for style file:" );
		my @xml = $self->get_root()->generate_full_xml();
		open FH , "> $filename" or die "Cannot create stylefile: $!";
		print FH "<?xml version=\"1.0\"?>\n<!DOCTYPE CITESTYLE PUBLIC \n\t\"-//Markus Hoenicka//DTD CiteStyle V1.4//EN\" \n\t\"http://refdb.sourceforge.net/dtd/citestylex-1.4/citestylex.dtd\">\n";
		print FH $xml[1];
		close FH;
		$self->set_style_file( $filename );
		printf "Style file successfully written to <%s>.\n" , $filename;
		return $filename;
	}

=item

B<$ui-E<gt>write_summary();>

Writes style summary to file.  Checks filename doesn't already exist.  If so, proposes alternative.

=cut

	sub write_summary {
		my $self = shift;
		print "\nOutputting style summary to file.\n";
		my $stylename = $self->get_root()->get_style_name();
		my $filename = $self->get_output_filename(
			sprintf( "%s-summary.html" , $stylename ) , 
			sprintf( "%s-summary-XXXX" , $stylename ) , 
			".html" ,
			"Enter filename for summary file:" );
		open FH , "> $filename" or die "Cannot create summary file: $!";
		print FH $self->get_root()->generate_summary();
		close FH;
		$self->set_summary_file( $filename );
		printf "Summary successfully written to <%s>.\n" , $filename;
		return $filename;
	}

=item

B<$ui-E<gt>get_command_base();>

Gets base for refdba commands.  Involves checking for access to refdba.  Also determines whether username and password required.

If succeeds, returns base command of the form:

	refdba [-u <username> -w <password>] -C

If fails, returns C<undef>.

=cut

	sub get_command_base {
		my ( $self ) = ( shift );
		my ( $command_base , %_ACCESS );
		print "\n";
		$self->_display( "The RefDB administrative client (refdba) needs the user's username and password whenever it is invoked.  To save typing them every time refdba is run some users put this information in refdba's configuration file.\n" );
		while ( 1 ) {
			my $choice;
			print "\n";
			while ( 1 ) {
				$choice = $self->_input_choice( "\nHas refdba been configured to know your username and password? ('No' if unsure)" , ( "Yes" , "No" , "Abort" ) );
				if ( defined( $choice ) ) { last; }
				print "Invalid choice.  Sorry, please try again.\n";
			}
			# SWITCH: $choice
			# case: 'No'
			( grep /\b$choice\b/ , ( "No" ) ) and do {
				my ( $username , $password );
				print "\n";
				while ( 1 ) {
					$username = $self->_input_ask( "Enter RefDB username:" , $_ACCESS{username} );
					last if $username;
				}
				$_ACCESS{username} = $username;
				while ( 1 ) {
					$password = $self->_input_ask( "Enter RefDB password:" , $_ACCESS{password} );
					last if $password;
				}
				$_ACCESS{password} = $password;
			};
			# case: 'Yes' | 'No'
			( grep /\b$choice\b/ , ( "Yes" , "No" ) ) and do {
				$command_base = "refdba";
				$command_base .= sprintf( " -u %s" , $_ACCESS{username} ) if $_ACCESS{username};
				$command_base .= sprintf( " -w %s" , $_ACCESS{password} ) if $_ACCESS{password};
				$command_base .= " -C";
				my $command = sprintf "%s liststyle" , $command_base;
				print "\nLet's test access to refdba by getting a list of current styles...\n--------------------\n";
				if ( !system $command ) {
					print "--------------------\nIt appears I have access to refdba.\n";
					last;
				} else {
					print "--------------------\nIt appears I am currently unable to access refdba.\n\nPossible causes include:\n - incorrect username or password,\n - you do not have access to, or permission to run, refdba,\n - the RefDB server (refdbd) is not running or is unreachable, and\n - RefDB is not installed on this system.\n\nLet's try again.\n";
					next;
				}
			};
			# case: 'Abort upload'
			( grep /\b$choice\b/ , ( "Abort" ) ) and do {
				$command_base = undef;
				last;
			};
			# case: default
			( not grep /\b$choice\b/ , ( "Yes" , "No" , "Abort" ) ) and do {
				die "No matching case block for \"$choice\"";
			};
		}
		return $command_base;
	}

=item

B<$ui-E<gt>upload_style();>

Uploads style to refdb.  If style of same name already exists, attempt to save it to disk.

=cut

	sub upload_style {
		my ( $self , $command_base , $command , $exit_status ) = ( shift );
		my $stylefile = $self->get_style_file();
		my $stylename = $self->get_root()->get_style_name();
		print "\n";
		return if not $self->_input_confirm( "Do you want to upload the style to RefDB now?\n\nYou will be asked before any styles are overwritten.\n\nThis process requires a running RefDB server (refdbd) and access to a RefDB administrative client (refdba).  If you don't understand the last sentence you should probably answer 'No' here and consult the RefDB manual and/or contact your system administrator." );
		$command_base = $self->get_command_base();
		if ( not $command_base ) {
			printf "\nUpload of style \"%s\" was cancelled.\n";
			return 0;
		}
		$command = sprintf "%s liststyle" , $command_base;
		my @stylelist = `$command`;
		if ( grep /\b$stylename\b/ , @stylelist ) {
			printf "\nThere is already a style named \"%s\".  I will back it up to a file.\n" , $stylename;
			my $filename = $self->get_output_filename(
				sprintf( "old-%s-style.xml" , $stylename ) , 
				sprintf( "old-%s-XXXX" , $stylename ) , 
				"-style.xml" ,
				"Enter filename for style backup file:" );
			$command = sprintf "%s getstyle -c stdout -o %s %s" , $command_base , $filename , $stylename;
			print "Backing up ...\n--------------------\n";
			$exit_status = !system $command;
			print "--------------------\n";
			if ( $exit_status && -f $filename && -s $filename ) {
				printf "Pre-existing style \"%s\" was backed up to <%s>.\n" , $stylename , $filename;
			} else {
				printf "The backup of pre-existing style \"%s\" was unsuccessful.\n" , $stylename;
				my $prompt = sprintf "Do you still want to upload style \"%s\"?\n\nThe pre-existing style of that name will be over-written." , $stylename;
				if ( not $self->_input_confirm( $prompt ) ) { return 0; }
			}
		}
		$command = sprintf "%s addstyle -c stdout %s" , $command_base , $stylefile;
		printf "Uploading style \"%s\"...\n--------------------\n" , $stylename;
		$exit_status = !system $command;
		print "--------------------\n";
		if ( $exit_status ) {
			printf "\nStyle \"%s\" successfully uploaded to refdb.\n" , $stylename;
		} else {
			print "\nUpload of style \"%s\" was unsuccessful.\n" , $stylename;
		}
		return;
	}

=back

=cut

1;

__END__

=head1 AUTHOR

David Nebauer, david E<lt>atE<gt> nebauer E<lt>dotE<gt> org

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2004 by David Nebauer

This library is distributed under the same license and conditions as the C<RefDB> project E<lt>L<http://refdb.sourceforge.net/>E<gt>.

=cut
