indexing description: "An HTML output file" copyright: "Copyright (c) 2003-2006, Julian Tschannen" license: "Eiffel Forum License v2 (see forum.txt)" date: "$Date$" revision: "$Revision$" class EDOC_HTML_OUTPUT_FILE inherit KL_TEXT_OUTPUT_FILE redefine make end EDOC_SHARED_CSS_CONSTANTS export {NONE} all end EDOC_SHARED_HTML_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 HTML 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_css (a_tag, a_css_class: STRING) is -- Print start tag `a_tag' with css attribute `a_css_class'. require a_tag_not_void: a_tag /= Void a_css_class_not_void: a_css_class /= Void open_write: is_open_write do start_tag_attributes (a_tag, << "class", a_css_class >>) 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 ("<"+a_tag+">%N") else put_string ("<"+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 ("%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_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 ("<"+a_tag+"/>%N") else put_string ("<"+a_tag+attributes_string (an_attributes)+"/>%N") 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_css_content (a_tag, a_css_class, a_content: STRING) is -- Single tag with a css class and content. -- Ends with newline. do content_line (tag_attributes_content_to_string (a_tag, << "class", a_css_class >>, 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_css_content_to_string (a_tag, a_css_class, a_content: STRING): STRING is -- Single tag with a css class and content. do Result := tag_attributes_content_to_string (a_tag, << "class", a_css_class >>, a_content) 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 := "<"+a_tag+">"+a_content+"" else Result := "<"+a_tag+attributes_string (an_attributes)+">"+a_content+"" 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 (""+a_content+"%N") end link_css_content (a_link, a_css_class, a_content: STRING) is -- Print a link to 'a_link' with 'a_css_class' and 'a_content'. require a_link_not_void: a_link /= Void a_css_class_not_void: a_css_class /= Void a_content_not_void: a_content /= Void do indent put_string (""+a_content+"%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 := ""+a_content+"" ensure result_not_void: Result /= Void end link_css_content_to_string (a_link, a_css_class, a_content: STRING): STRING is -- Print a link to 'a_link' with 'a_css_class' and 'a_content' to a string. do Result := ""+a_content+"" 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_html_head (a_title: STRING; a_css_stylesheet: 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 do put_string ("%N") put_string ("%N") start_tag_attributes ("html", << "xmlns", "http://www.w3.org/1999/xhtml" >>) start_tag ("head") tag_content ("title", a_title+" ("+Options.short_title+")") tag_attributes ("link", << "rel", "stylesheet", "type", "text/css", "href", a_css_stylesheet >>) end_tag 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 := html_context.cluster_link_by_cluster (a_cluster) if a_link = Void then Result := tag_css_content_to_string ("span", css_cluster_name, a_cluster.name) else Result := link_css_content_to_string (a_link, css_linked_cluster_name, 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 := html_context.cluster_link_by_cluster (a_cluster) if a_link = Void then tag_css_content ("span", css_cluster_name, a_content) else link_css_content (a_link, css_cluster_name, 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 if link_a_cluster then create Result.make_from_string (print_cluster_name_to_string (a_cluster)) else create Result.make_from_string (tag_css_content_to_string ("span", css_cluster_name, a_cluster.name)) end from a_parent := a_cluster.parent until a_parent = Void loop Result := STRING_.concat (print_cluster_name_to_string (a_parent)+".", Result) a_parent := a_parent.parent 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 := html_context.class_link_by_class (a_class) if a_link = Void then Result := tag_css_content_to_string ("span", css_class_name, a_class.name.name) else Result := link_css_content_to_string (a_link, css_linked_class_name, 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. local title: STRING do if Options.home_url /= Void and then Options.home_url.count > 0 then title := link_css_content_to_string (Options.home_url, css_button, Options.short_title) else title := Options.short_title end start_tag_css ("div", css_header) if Options.version /= Void and then Options.version.count > 0 then tag_attributes_content ("div", << "class", css_header_title, "id", "top" >> , title+" "+Options.version) else tag_attributes_content ("div", << "class", css_header_title, "id", "top" >> , title) end end print_header_end is -- End the header. -- Call `print_header_start' before. do end_tag tag ("hr") 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_css_content_to_string (Options.home_url, css_button, Options.short_title) else title := Options.short_title end tag ("hr") start_tag ("p") if Options.is_edoc_notice_printed then link_css_content (Options.edoc_website_link, css_edoc_notice, "Documentation generated by edoc") tag ("br") end tag ("br") end_tag start_tag_css ("div", css_footer) if Options.version /= Void and then Options.version.count > 0 then tag_attributes_content ("div", << "class", css_header_title >> , title+" "+Options.version) else tag_attributes_content ("div", << "class", css_header_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_css ("div", css_buttons) if Options.is_overview_generated then print_button ("Overview", html_context.overview_page_link) end if Options.is_clusters_file_generated then print_button ("Clusters", html_context.clusters_page_link) end if Options.is_classes_file_generated then print_button ("Classes", html_context.classes_page_link) end if Options.are_cluster_files_generated then print_button ("Cluster", html_context.cluster_page_link) end print_button ("Class", html_context.class_page_link) if Options.are_usage_files_generated then print_button ("Usage", html_context.usage_page_link) end if Options.is_index_generated then print_button ("Index", html_context.index_page_link) end tag_content ("span", "        ") link_css_content ("#top", css_button, "Top") if Options.is_feature_list_generated and then html_context.class_page_link /= Void and then html_context.class_page_link.is_equal (html_context.Active_page_link) then link_css_content ("#features-list", css_button, "Features") 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_css_content ("span", css_button, a_title) elseif a_link.is_equal (html_context.Active_page_link) then tag_css_content ("span", css_active_button, a_title) else link_css_content (a_link, css_button, a_title) 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 ("li") print_qualified_cluster_name_single_link (a_item) end_tag 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 do create splitter.make splitter.set_separators ("%R%N") start_tag_css ("div", css_comment) start_tag ("p") 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 ("p") 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) if lines.after or not add_breaks then content_line (processed_line) else content_line (processed_line+"
") end end end end_tag end_tag 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 := html_context.feature_link_by_feature (a_feature, Void) if a_link /= Void then replacement := link_css_content_to_string (a_link, css_linked_feature_name, substring) else replacement := tag_css_content_to_string ("span", css_commented_feature_name, substring) end else replacement := tag_css_content_to_string ("span", css_commented_feature_name, 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 invariant name_not_void: name /= Void open_tags_not_void: open_tags /= Void end