summaryrefslogtreecommitdiffstats
path: root/lib/MarkdownBook/Document.pm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/MarkdownBook/Document.pm')
-rw-r--r--lib/MarkdownBook/Document.pm245
1 files changed, 245 insertions, 0 deletions
diff --git a/lib/MarkdownBook/Document.pm b/lib/MarkdownBook/Document.pm
new file mode 100644
index 0000000..2a30396
--- /dev/null
+++ b/lib/MarkdownBook/Document.pm
@@ -0,0 +1,245 @@
+# Copyright (C) 2012 Patrick "P. J." McDermott
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+use strict;
+use warnings;
+
+use MarkdownBook::Section;
+use Carp;
+
+package MarkdownBook::Document;
+
+sub new
+{
+ my ($class, $book, $type, $file, $id, $title) = @_;
+ my $self;
+
+ $class = ref($class) || $class;
+ $self = {};
+ bless($self, $class);
+
+ $self->{'book'} = $book;
+ $self->{'type'} = $type;
+ $self->{'file'} = $file;
+ $self->{'id'} = $id;
+ $self->{'title'} = $title;
+
+ return $self;
+}
+
+sub book
+{
+ my ($self, $book) = @_;
+ my $old = $self->{'book'};
+
+ $self->{'book'} = $book if defined($book);
+
+ return $old;
+}
+
+sub type
+{
+ my ($self, $type) = @_;
+ my $old = $self->{'type'};
+
+ $self->{'type'} = $type if defined($type);
+
+ return $old;
+}
+
+sub file
+{
+ my ($self, $file) = @_;
+ my $old = $self->{'file'};
+
+ $self->{'file'} = $file if defined($file);
+
+ return $old;
+}
+
+sub file_path
+{
+ my ($self) = @_;
+
+ return $self->{'book'}->dir() . '/' . $self->{'file'};
+}
+
+sub id
+{
+ my ($self) = @_;
+
+ return $self->{'id'};
+}
+
+sub title
+{
+ my ($self, $title) = @_;
+ my $old = $self->{'title'};
+
+ $self->{'title'} = $title if defined($title);
+
+ return $old;
+}
+
+sub full_title
+{
+ my ($self) = @_;
+
+ if ($self->{'type'} eq 'chapter') {
+ return sprintf('Chapter %d - %s', $self->{'id'}, $self->{'title'});
+ } elsif ($self->{'type'} eq 'appendix') {
+ return sprintf('Appendix %s - %s', $self->{'id'}, $self->{'title'});
+ } else {
+ return undef;
+ }
+}
+
+sub prev
+{
+ my ($self, $other) = @_;
+ my $old = $self->{'prev'};
+
+ $self->{'prev'} = $other if defined($other);
+
+ return $old;
+}
+
+sub next
+{
+ my ($self, $other) = @_;
+ my $old = $self->{'next'};
+
+ $self->{'next'} = $other if defined($other);
+
+ return $old;
+}
+
+sub sections
+{
+ my ($self) = @_;
+
+ # FIXME: Why is this necessary?!
+ foreach my $sec (@{$self->{'sections'}}) {
+ }
+
+ return $self->{'sections'};
+}
+
+sub parse
+{
+ my ($self) = @_;
+ my $source_fh;
+ my $source_text;
+
+ open($source_fh, '<',
+ $self->{'book'}->dir() . '/' . $self->{'file'} . '.mdwn')
+ or Carp::croak('Cannot open "' . $self->{'file'} . '" source document');
+ $source_text = join('', <$source_fh>);
+ close($source_fh);
+
+ # Parse headings of non-index documents.
+ if ($self->{'type'} ne 'index') {
+ $self->{'section_level_numbers'} = [0, 0];
+ $self->{'section_level'} = -1;
+ $source_text =~ s/
+ ^
+ (.+) # Heading text
+ [ \t]* # Optional trailing whitespace
+ \n # Line break
+ (=+|-+) # Underline
+ [ \t]* # Optional trailing whitespace
+ $
+ /$self->_do_heading($1, $2)/mexg;
+ }
+
+ # Store parsed text.
+ $self->{'source_text'} = $source_text;
+}
+
+sub _do_heading
+{
+ my ($self, $text, $underline) = @_;
+ my $level;
+ my $levels;
+ my $section_number;
+ my $section_title;
+ my $section_id;
+ my $section;
+
+ # Shorten underline to one character.
+ $underline =~ s/^([=-]).*$/$1/;
+
+ # Detect heading level.
+ if ($underline eq '=') {
+ $level = 1;
+ } else {
+ $level = 2;
+ }
+
+ # Calculate section number.
+ $levels = $#{$self->{'section_level_numbers'}};
+ if ($level != $self->{'section_level'}) {
+ foreach (@{$self->{'section_level_numbers'}}[$level .. $levels]) {
+ $_ = 0;
+ }
+ }
+ $self->{'section_level'} = $level;
+ ++${$self->{'section_level_numbers'}}[$level - 1];
+ $section_number = join('.', @{$self->{'section_level_numbers'}});
+
+ # Add document ID to section number.
+ $section_number = $self->{'id'} . '.' . $section_number;
+
+ # Trim off unused subsection parts.
+ $section_number =~ s/(?:\.0)*$//;
+
+ # Parse out section title.
+ $section_title = $text;
+ $section_title =~ s/
+ ^
+ ([^\[]+) # Section title
+ [ \t]+ # Whitespace
+ \[ # Left square bracket
+ [^\]]+ # Section ID
+ \] # Right square bracket
+ $
+ /$1/x;
+
+ # Parse out section ID.
+ $section_id = $text;
+ $section_id =~ s/
+ ^
+ [^\[\]]+ # Section title
+ [ \t]+ # Whitespace
+ \[ # Left square bracket
+ ([^\]]+) # Section ID
+ \] # Right square bracket
+ $
+ /$1/x;
+
+ # Create and store section object.
+ $section = MarkdownBook::Section->new($self,
+ $level, $section_number, $section_id, $section_title);
+ push(@{$self->{'sections'}}, $section);
+ $self->{'book'}->add_section($section);
+
+ # Prepend number to section title.
+ $text = $section_number . ' ' . $section_title;
+
+ # Return underlined section title.
+ return $text . "\n" . $underline x length($text);
+}
+
+1;