indexing
description:
"An XML output file"
copyright: "Copyright (c) 2007-2010, Beat Herlig"
license: "Eiffel Forum License v2 (see forum.txt)"
author: "bherlig"
date: "$Date$"
revision: "$Revision$"
class EDOC_XML_OUTPUT_FILE
inherit
KL_TEXT_OUTPUT_FILE
redefine
make
end
EDOC_SHARED_XML_CONTEXT
export {NONE} all end
EDOC_SHARED_ACCESS
export {NONE} all end
KL_SHARED_FILE_SYSTEM
export {NONE} all end
KL_IMPORTED_STRING_ROUTINES
export {NONE} all end
KL_IMPORTED_CHARACTER_ROUTINES
export {NONE} all end
create
make
feature {NONE} -- Initialisation
make (a_name: like name) is
-- Create a new file named `a_name'.
-- (`a_name' should follow the pathname convention
-- of the underlying platform. For pathname conversion
-- use KI_FILE_SYSTEM.pathname_from_file_system.)
do
Precursor {KL_TEXT_OUTPUT_FILE} (a_name)
if not file_system.directory_exists (file_system.dirname (a_name)) then
file_system.recursive_create_directory (file_system.dirname (a_name))
end
create open_tags.make
ensure then
name_set: name = a_name
end
feature -- Access
open_tags: DS_LINKED_STACK [STRING]
-- Open XML tags
feature -- Basic operations
indent is
-- Indent.
require
open_write: is_open_write
local
a_string: STRING
do
-- TODO: activate indentation only for html-debugging and disable when released to reduce doc-size
create a_string.make_filled (' ', open_tags.count*2)
put_string (a_string)
end
start_tag (a_tag: STRING) is
-- Print start tag `a_tag'.
require
a_tag_not_void: a_tag /= Void
open_write: is_open_write
do
start_tag_attributes (a_tag, Void)
end
start_tag_attributes (a_tag: STRING; an_attributes: ARRAY [STRING]) is
-- Print start tag `a_tag' with attributes `an_attributes'.
require
a_tag_not_void: a_tag /= Void
an_attributes_count_even: an_attributes /= Void implies an_attributes.count \\ 2 = 0
open_write: is_open_write
do
indent
if an_attributes = Void then
put_string ("<"+xml_context.edoc_namespace+":"+a_tag+">%N")
else
put_string ("<"+xml_context.edoc_namespace+":"+a_tag+attributes_string (an_attributes)+">%N")
end
open_tags.put (a_tag)
end
content_line (a_line: STRING) is
-- Print content line.
require
a_line_not_void: a_line /= Void
open_write: is_open_write
do
indent
put_string (a_line)
put_new_line
end
end_tag is
-- Print end tag.
require
open_write: is_open_write
local
a_tag: STRING
do
a_tag := open_tags.item
open_tags.remove
indent
put_string (""+xml_context.edoc_namespace+":"+a_tag+">%N")
end
tag (a_tag: STRING) is
-- Print single tag.
require
a_tag_not_void: a_tag /= Void
open_write: is_open_write
do
tag_attributes (a_tag, Void)
end
tag_to_string (a_tag: STRING): STRING is
-- Returns single tag.
require
a_tag_not_void: a_tag /= Void
do
Result := tag_attributes_to_string (a_tag, Void)
end
tag_attributes (a_tag: STRING; an_attributes: ARRAY [STRING]) is
-- Print single tag with attributes.
-- Ends with newline.
require
a_tag_not_void: a_tag /= Void
an_attributes_count_even: an_attributes /= Void implies an_attributes.count \\ 2 = 0
open_write: is_open_write
do
indent
if an_attributes = Void then
put_string ("<"+xml_context.edoc_namespace+":"+a_tag+"/>%N")
else
put_string ("<"+xml_context.edoc_namespace+":"+a_tag+attributes_string (an_attributes)+"/>%N")
end
end
tag_attributes_to_string (a_tag: STRING; an_attributes: ARRAY [STRING]): STRING is
-- Returns single tag with attributes, including newline character
require
a_tag_not_void: a_tag /= Void
an_attributes_count_even: an_attributes /= Void implies an_attributes.count \\ 2 = 0
open_write: is_open_write
do
create Result.make_empty
if an_attributes = Void then
Result.append ("<"+xml_context.edoc_namespace+":"+a_tag+"/>")
else
Result.append ("<"+xml_context.edoc_namespace+":"+a_tag+attributes_string (an_attributes)+"/>")
end
end
tag_content (a_tag, a_content: STRING) is
-- Singe tag with content.
-- Ends with newline.
do
content_line (tag_content_to_string (a_tag, a_content))
end
tag_attributes_content (a_tag: STRING; an_attributes: ARRAY [STRING]; a_content: STRING) is
-- Single tag with attributes and content.
-- Ends with newline.
require
open_write: is_open_write
a_tag_not_void: a_tag /= Void
a_content_not_void: a_content /= Void
do
content_line (tag_attributes_content_to_string (a_tag, an_attributes, a_content))
end
tag_content_to_string (a_tag, a_content: STRING): STRING is
-- String of single tag with content.
require
a_tag_not_void: a_tag /= Void
a_content_not_void: a_content /= Void
do
Result := tag_attributes_content_to_string (a_tag, Void, a_content)
ensure
result_not_void: Result /= Void
end
tag_attributes_content_to_string (a_tag: STRING; an_attributes: ARRAY [STRING]; a_content: STRING): STRING is
-- Single tag with attributes and content.
require
a_tag_not_void: a_tag /= Void
an_attributes_count_even: an_attributes /= Void implies an_attributes.count \\ 2 = 0
a_content_not_void: a_content /= Void
open_write: is_open_write
do
if an_attributes = Void then
Result := "<"+xml_context.edoc_namespace+":"+a_tag+">"+a_content+""+xml_context.edoc_namespace+":"+a_tag+">"
else
Result := "<"+xml_context.edoc_namespace+":"+a_tag+attributes_string (an_attributes)+">"+a_content+""+xml_context.edoc_namespace+":"+a_tag+">"
end
ensure
result_not_void: Result /= Void
end
link_content (a_link, a_content: STRING) is
-- Print a link to 'a_link' with 'a_content'.
require
a_link_not_void: a_link /= Void
a_content_not_void: a_content /= Void
do
indent
put_string ("<"+xml_context.edoc_namespace+":link url=%""+a_link+"%">"+a_content+""+xml_context.edoc_namespace+":link>%N")
end
link_content_to_string (a_link, a_content: STRING): STRING is
-- Print a link to 'a_link' with 'a_content' to a string.
require
a_link_not_void: a_link /= Void
a_content_not_void: a_content /= Void
do
Result := "<"+xml_context.edoc_namespace+":link url=%""+a_link+"%">"+a_content+""+xml_context.edoc_namespace+":link>%N"
ensure
result_not_void: Result /= Void
end
anchor_content (a_name, a_content: STRING) is
-- Print an anchor with 'a_name' and 'a_content' to a string.
do
indent
put_string (anchor_content_to_string (a_name, a_content)+"%N")
end
anchor_content_to_string (a_name, a_content: STRING): STRING is
-- Print an anchor with 'a_name' and 'a_content' to a string.
do
Result := ""+a_content+""
end
feature -- Conveniance printing
print_xml_head (a_title: STRING; an_xsd_link: STRING) is
-- Print an html tag with a head section with `a_title' and `a_css_stylesheet'.
require
a_title_not_void: a_title /= Void
open_write: is_open_write
local
xml_attribute_string: STRING
do
if options.xml_header_file /= Void then
-- custom header
print_whole_file(options.xml_header_file)
else
-- default xml header
put_line ("%N")
put_line ("")
end
-- print root element directly to the file, add manually to tag-stack.
-- this way, the default namespace won't get added to the root element,
-- nor to the tag-stack
-- will/has to be closed in feature `print_xml_footer'
xml_attribute_string := "xmlns:edoc=%"http://edoc.origo.ethz.ch%"%N%T"
xml_attribute_string.append ("xmlns:xsi=%"http://www.w3.org/2001/XMLSchema-instance%"%N%T")
xml_attribute_string.append ("xsi:schemaLocation=%"http://edoc.origo.ethz.ch " + an_xsd_link + "%"")
content_line ("")
tag_content ("title", a_title +" ("+Options.short_title+")")
start_tag ("body") -- will/has to be closed in feature `print_xml_footer'
end
print_xml_footer is
-- Print an xml footer
require
open_write: is_open_write
do
end_tag -- body
if options.xml_footer_file /= Void then
-- custom footer
print_whole_file(options.xml_footer_file)
else
-- default footer
end
content_line ("")
end
print_cluster_name (a_cluster: ET_CLUSTER) is
-- Print class name of 'a_cluster' to string.
do
put_string (print_cluster_name_to_string (a_cluster))
end
print_cluster_name_to_string (a_cluster: ET_CLUSTER): STRING is
-- Print cluster name of 'a_cluster' to string.
require
a_cluster_not_void: a_cluster /= Void
local
a_link: STRING
do
a_link := xml_context.cluster_link_by_cluster (a_cluster)
if a_link = Void then
Result := tag_content_to_string ("cluster", a_cluster.name)
else
Result := tag_attributes_content_to_string ("cluster", << "url", a_link>>, a_cluster.name)
end
ensure
result_exists: Result /= Void
end
print_qualified_cluster_name_single_link (a_cluster: ET_CLUSTER) is
-- Print qualified cluster name of 'a_cluster' as a link to 'a_cluster'.
require
a_cluster_not_void: a_cluster /= Void
local
a_parent: ET_CLUSTER
a_content: STRING
a_link: STRING
do
create a_content.make_from_string (a_cluster.name)
from
a_parent := a_cluster.parent
until
a_parent = Void
loop
a_content := STRING_.concat (a_parent.name+".", a_content)
a_parent := a_parent.parent
end
indent
a_link := xml_context.cluster_link_by_cluster (a_cluster)
if a_link = Void then
tag_content ("cluster", a_content)
else
tag_attributes_content ("cluster", << "url", a_link>>, a_content)
end
end
print_qualified_cluster_name_to_string (a_cluster: ET_CLUSTER; link_a_cluster: BOOLEAN): STRING is
-- Print cluster name of 'a_cluster' and all its parents to string.
require
a_cluster_not_void: a_cluster /= Void
local
a_parent: ET_CLUSTER
do
create Result.make_empty
a_parent := a_cluster.parent
if a_parent /= Void then
if link_a_cluster then
Result.append (print_cluster_name_to_string (a_cluster))
else
Result.append (tag_content_to_string ("cluster", a_cluster.name))
end
from
until
a_parent = Void
loop
Result := STRING_.concat (print_cluster_name_to_string (a_parent)+".", Result)
a_parent := a_parent.parent
end
Result := tag_content_to_string ("cluster_qualified_name", Result)
end
ensure
result_exists: Result /= Void
end
print_class_name (a_class: ET_CLASS) is
-- Print class name of 'a_class'.
do
put_string (print_class_name_to_string (a_class))
end
print_class_name_to_string (a_class: ET_CLASS): STRING is
-- Print class name of 'a_class' to string.
require
a_class_not_void: a_class /= Void
local
a_link: STRING
do
a_link := xml_context.class_link_by_class (a_class)
if a_link = Void then
Result := tag_content_to_string ("class", a_class.name.name)
else
Result := tag_attributes_content_to_string ("class", << "url", a_link>>, a_class.name.name)
end
ensure
result_exists: Result /= Void
end
print_qualified_class_name (a_class: ET_CLASS) is
-- Print qualified class name of 'a_class'.
do
put_string (print_qualified_class_name_to_string (a_class))
end
print_qualified_class_name_to_string (a_class: ET_CLASS): STRING is
-- Print qualified class name of 'a_class' to string.
do
Result := print_qualified_cluster_name_to_string (a_class.group.cluster, True)+"."+print_class_name_to_string (a_class)
end
print_header_start is
-- Start with the header.
-- Call `print_header_end' to finish the header.
do
start_tag ("header")
if Options.home_url /= Void and then Options.home_url.count > 0 then
tag_attributes_content ("title", << "url", options.home_url>>, options.short_title)
else
tag_content ("title", options.short_title)
end
print_buttons
end
print_header_end is
-- ends the header.
do
end_tag -- header
end
print_footer_start is
-- Start with the footer.
-- Call `print_footer_end' to finish the footer.
local
title: STRING
do
if Options.home_url /= Void and then Options.home_url.count > 0 then
title := link_content_to_string (Options.home_url, Options.short_title)
else
title := Options.short_title
end
start_tag ("footer")
if Options.is_edoc_notice_printed then
link_content (Options.edoc_website_link, "Documentation generated by edoc")
end
if Options.version /= Void and then Options.version.count > 0 then
tag_content ("version", title+" "+Options.version)
else
tag_content ("footer_title", title)
end
end
print_footer_end is
-- End the footer.
-- Call `print_footer_start' before.
do
end_tag
end
print_buttons is
-- Print buttons.
do
start_tag ("button_list")
if Options.is_overview_generated then
print_button ("Overview", xml_context.overview_page_link)
end
if Options.is_clusters_file_generated then
print_button ("Clusters", xml_context.clusters_page_link)
end
if Options.is_classes_file_generated then
print_button ("Classes", xml_context.classes_page_link)
end
if Options.are_cluster_files_generated then
print_button ("Cluster", xml_context.cluster_page_link)
end
print_button ("Class", xml_context.class_page_link)
if Options.are_usage_files_generated then
print_button ("Usage", xml_context.usage_page_link)
end
if Options.is_index_generated then
print_button ("Index", xml_context.index_page_link)
end
start_tag_attributes ("button", << "url", "#top" >>)
content_line ("Top")
end_tag
if Options.is_feature_list_generated and then xml_context.class_page_link /= Void and then xml_context.class_page_link.is_equal (xml_context.Active_page_link) then
start_tag_attributes ("button", << "url", "#features-list" >>)
content_line ("Features")
end_tag
end
end_tag
end
print_button (a_title, a_link: STRING) is
-- Print button.
require
a_title_not_void: a_title /= Void
do
if a_link = Void then
tag_content ("inactive_button", a_title)
elseif a_link.is_equal (xml_context.Active_page_link) then
tag_content ("active_button", a_title)
else
start_tag_attributes ("button", << "url", a_link >>)
content_line (a_title)
end_tag
end
end
print_subclusters_tree (a_cluster: ET_CLUSTER) is
-- Print tree of subclusters of 'a_cluster'.
require
a_cluster_not_void: a_cluster /= Void
local
cluster_cursor: DS_LIST_CURSOR [ET_CLUSTER]
a_item: ET_CLUSTER
do
if a_cluster.subclusters /= Void then
Cluster_sorter.sort (a_cluster.subclusters.clusters)
cluster_cursor := a_cluster.subclusters.clusters.new_cursor
from
cluster_cursor.start
until
cluster_cursor.after
loop
a_item := cluster_cursor.item
if Context.documented_clusters.has (a_item) then
start_tag ("listitem")
start_tag ("cluster_qualified_name")
print_qualified_cluster_name_single_link (a_item)
end_tag -- cluster_qualified_name
end_tag -- listitem
print_subclusters_tree (a_item)
end
cluster_cursor.forth
end
end
end
print_comment (a_string: STRING; a_class: ET_CLASS; a_features_list: DS_LIST [ET_FEATURE]; add_breaks: BOOLEAN) is
-- Print `a_string' as comment.
-- Set links to features listed in `a_features_list'.
-- When called from the class printer provide `a_class' as current class.
require
a_string_not_void: a_string /= Void
a_features_list_not_void: a_features_list /= Void
local
-- lines: DS_LIST [STRING]
-- splitter: ST_SPLITTER
-- processed_line: STRING
-- printed_empty_line: BOOLEAN
a_comment: STRING
do
a_comment := a_string.twin
replace_features_in_comment (a_comment, a_class, a_features_list)
replace_classes_in_comment (a_comment)
-- start_tag ("comment")
-- content_line (a_comment)
-- end_tag
tag_content ("comment", a_comment)
-- create splitter.make
-- splitter.set_separators ("%R%N")
-- start_tag ("comment")
-- lines := splitter.split_greedy (a_string)
-- from
-- lines.start
-- until
-- lines.after
-- loop
-- processed_line := lines.item_for_iteration
-- STRING_.left_adjust (processed_line)
-- STRING_.right_adjust (processed_line)
-- lines.forth
-- if processed_line.is_empty then
-- if not printed_empty_line then
-- end_tag
-- start_tag ("comment")
-- printed_empty_line := True
-- end
-- else
-- printed_empty_line := False
-- replace_features_in_comment (processed_line, a_class, a_features_list)
-- replace_classes_in_comment (processed_line)
-- content_line (processed_line)
---- if lines.after or not add_breaks then
---- content_line (processed_line)
---- else
---- content_line (processed_line+"
")
---- end
-- end
-- end
-- end_tag -- comment
end
feature {NONE} -- Implementation
replace_features_in_comment (a_string: STRING; a_class: ET_CLASS; a_features_list: DS_LIST [ET_FEATURE]) is
-- Replace features in `a_string' with links.
require
a_string_not_void: a_string /= Void
a_features_list_not_void: a_features_list /= Void
local
i, j: INTEGER
a_feature: ET_FEATURE
substring, replacement, a_link: STRING
do
from i := 1; j := 1 until i = 0 or j = 0 loop
i := a_string.index_of ('`', j)
j := a_string.index_of ('%'', i+1)
if i > 0 and j > 0 then
substring := a_string.substring (i+1, j-1)
if a_class /= Void then
a_feature := a_class.named_feature (create {ET_IDENTIFIER}.make (substring))
end
if a_feature /= Void and then a_features_list.has (a_feature) then
a_link := xml_context.feature_link_by_feature (a_feature, Void)
if a_link /= Void then
replacement := link_content_to_string (a_link, substring)
else
replacement := tag_content_to_string ("feature", substring)
end
else
replacement := tag_content_to_string ("feature", substring)
end
a_string.replace_substring (replacement, i, j)
j := j + replacement.count - substring.count - 2
end
end
end
replace_classes_in_comment (a_string: STRING) is
-- Replace classes in `a_string' with links.
require
a_string_not_void: a_string /= Void
local
i, j: INTEGER
in_word: BOOLEAN
substring, replacement: STRING
a_class: ET_CLASS
do
j := 0
from i := 1 until i > a_string.count loop
if is_valid_word_character (a_string.item (i)) then
if not in_word then
in_word := True
j := i
end
else
if in_word then
in_word := False
substring := a_string.substring (j, i-1)
if is_valid_classname (substring) then
a_class := Context.universe.class_by_name (substring)
if a_class /= Void then
replacement := print_class_name_to_string (a_class)
a_string.replace_substring (replacement, j, i-1)
i := i + replacement.count - substring.count
end
end
end
end
i := i + 1
end
if in_word then
substring := a_string.substring (j, i-1)
if is_valid_classname (substring) then
a_class := Context.universe.class_by_name (substring)
if a_class /= Void then
replacement := print_class_name_to_string (a_class)
a_string.replace_substring (replacement, j, i-1)
i := i + replacement.count - substring.count
end
end
end
end
is_valid_word_character (a_character: CHARACTER): BOOLEAN is
-- Is `a_character' a valid classname character?
do
Result := (a_character >= '0' and a_character <= '9') or a_character.is_upper or a_character.is_lower or a_character = '_'
end
is_valid_classname (a_string: STRING): BOOLEAN is
-- Is `a_string' a valid classname to be replaced?
require
a_string_not_void: a_string /= Void
local
i, nb: INTEGER
do
Result := True
nb := a_string.count
from i := 1 until i > nb or not Result loop
if a_string.item (i).is_lower then
Result := False
end
i := i + 1
end
end
attributes_string (a_attributes: ARRAY [STRING]): STRING is
-- Start new tag `a_tag' with attributes `a_attributes'.
require
a_attributes_not_void: a_attributes /= Void
local
attribute_name, attribute_value: STRING
i: INTEGER
do
create Result.make_empty
from
i := 1
variant
a_attributes.count + 1 - i
until
i > a_attributes.count
loop
attribute_name := a_attributes.item (i)
attribute_value := a_attributes.item (i+1)
Result := STRING_.concat (Result, " "+attribute_name+"=%""+attribute_value+"%"")
i := i + 2
end
end
print_whole_file(a_filename: STRING) is
-- Prints all content from `a_filename' to the output file.
require
a_string_not_void: a_filename /= Void
local
in_file: KI_TEXT_INPUT_FILE
do
if file_system.file_exists (a_filename) then
-- read file
in_file := file_system.new_input_file (a_filename)
from in_file.open_read
until in_file.end_of_file
loop
in_file.read_line
put_line (in_file.last_string)
end
in_file.close
else
Error_handler.raise_warning (Error_handler.error_file_not_found, << a_filename >>)
end
end
invariant
name_not_void: name /= Void
open_tags_not_void: open_tags /= Void
end