{"id":131,"date":"2021-01-19T19:44:53","date_gmt":"2021-01-19T19:44:53","guid":{"rendered":"https:\/\/bhiggs.x10hosting.com\/HighOctaneJava\/?page_id=131"},"modified":"2021-01-19T19:49:10","modified_gmt":"2021-01-19T19:49:10","slug":"filter-streams","status":"publish","type":"page","link":"https:\/\/bhiggs.x10hosting.com\/HighOctaneJava\/course-topics\/i-o-files-streams\/filter-streams\/","title":{"rendered":"Filter Streams"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Here&#8217;s an example of how you might write a filter stream &#8212; here, a <code><strong>FilterReader<\/strong><\/code>. This one filters in such a way that it causes Java-style comments to be stripped from the stream as it passes through the filter:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; auto-links: false; title: ; quick-code: false; notranslate\" title=\"\">\npackage inputOutput;\n\nimport java.io.BufferedReader;\nimport java.io.BufferedWriter;\nimport java.io.FileReader;\nimport java.io.FileWriter;\nimport java.io.IOException;\nimport java.io.IOException;\nimport java.io.OutputStreamWriter;\nimport java.io.PrintWriter;\nimport java.io.PushbackReader;\nimport java.io.Reader;\n\n\/**\n *  This class filters out Java-style comments.\n *  It replaces &quot;\/*&quot; comments with a single space,\n *  and &quot;\/\/&quot; comments with a newline.\n *\n *  NOTE: The algorithm used here is not perfect;\n *        It should ignore cases of &quot;\/*&quot; and &quot;\/\/&quot; within literal \n *        strings. Perfecting the algorithm is left as an exercise\n *        for the reader (pun intended!).\n *\n *  @author B. J. Higgs\n *\/\npublic class CommentFilterReader extends PushbackReader\n{\n    public CommentFilterReader(Reader in)\n    {\n        super(in);\n    }\n    \n    \/**\n     *  Read a single character. \n     *  This method will block until a character is available, \n     *  an I\/O error occurs, or the end of the stream is reached. \n     *\n     *  @return The character read, as an integer in the range \n     *          0 to 16383 (0x00-0xffff), \n     *          or -1 if the end of the stream has been reached\n     *\/\n    public int read() \n        throws IOException\n    {\n        int c = readNextNonCommentChar();\n        return c;\n    }\n    \n    \/**\n     *  Reads the input, and returns the next non-comment character.\n     *  It detects the start and end of both kinds of Java comments,\n     *  and skips over comment characters as it detects them.\n     *\n     *  @return The character read, as an integer in the range \n     *          0 to 16383 (0x00-0xffff), \n     *          or -1 if the end of the stream has been reached\n     *\/\n    private int readNextNonCommentChar() \n        throws IOException\n    {\n        int c = super.read();\n        if (c == &#039;\/&#039;)\n        {\n            \/\/ Possible start of comment, so check for &#039;*&#039; or &#039;\/&#039;\n            int lookAheadChar = super.read();\n            switch (lookAheadChar)\n            {\n                case &#039;*&#039;:\n                    \/\/ We&#039;ve found a &#039;\/*&#039; comment, so silently\n                    \/\/ consume characters until we see a &#039;*\/&#039;\n                    while (true)\n                    {\n                        c = super.read();\n                        if (c == -1)\n                            break;      \/\/ End of stream\n                        if (c == &#039;*&#039;)\n                        {\n                            lookAheadChar = super.read();\n                            if (lookAheadChar == &#039;\/&#039;)\n                            {\n                                c = &#039; &#039;; \/\/ Replace comment with space\n                                break;  \/\/ End of comment\n                            }\n                        }\n                    }\n                    break;\n                        \n                case &#039;\/&#039;:\n                    \/\/ We&#039;ve found a &#039;\/\/&#039; comment, so silently\n                    \/\/ consume characters until we see a newline\n                    while (true)\n                    {\n                        c = super.read();\n                        if (c == -1)\n                            break;      \/\/ End of stream\n                        if (c == &#039;\\n&#039;)\n                        {\n                            \/\/ Newline ends comment -- \n                            \/\/ return the newline\n                            break;\n                        }\n                    }\n                    break;\n                        \n                default:\n                    \/\/ Not a command, so push back the look \n                    \/\/ ahead char so it gets read on the next read.\n                    unread(lookAheadChar);\n                    break;\n            }\n        }\n        \n        \/\/ No matter how we get here, \n        \/\/ c will contain the character to return.\n        return c;\n    }\n    \n    \/**\n     *  Read characters into a portion of an array. \n     *  This method will block until some input is available,\n     *  an I\/O error occurs, or the end of the stream is reached.\n     *\n     *  @param cbuf - Destination buffer \n     *  @param off - Offset at which to start storing characters \n     *  @param len - Maximum number of characters to read \n     *  @return The number of characters read, \n     *          or -1 if the end of the stream has been reached\n     *\/\n    public int read(char&#x5B;] cbuf, int off, int len) \n        throws IOException\n    {\n        int count = 0;\n        \/\/ For loop takes into account the available size of the \n        \/\/ buffer as well as the maximum number of characters to copy.\n        for (int avail = cbuf.length - off; \n\t   len &gt; 0 &amp;&amp; avail &gt; 0; \n\t   len--, avail--)\n        {\n            int c = readNextNonCommentChar();\n            if (c == -1)\n            {\n                \/\/ If this read didn&#039;t read any characters, \n                \/\/ return end of stream; otherwise, return \n                \/\/ the actual number read. (The next call will\n                \/\/ return end of stream.)\n                if (count == 0)\n                    count = -1;\n                break;\n            }\n            \/\/ Move character into buffer and keep count and \n            \/\/ offset updated.\n            cbuf&#x5B;off++] = (char)c;\n            count++;\n        }\n        \n        return count;\n    }\n    \n    \/**\n     *  Skip non-comment characters. \n     *  This method will block until some characters are available, \n     *  an I\/O error occurs, or the end of the stream is reached.\n     *\n     *  @param n The number of characters to skip\n     *  @return The number of characters actually skipped\n     *\/\n    public long skip(long n) \n        throws IOException\n    {\n        long count = 0;\n        for (count = 0; count &lt; n; count++)\n        {\n            int c = readNextNonCommentChar();\n            if (c == -1)\n                break;\n        }\n        \n        return count;\n    }\n\n\n    \/**\n     *  Main entry point, for testing.\n     *  Expects one or two arguments:\n     *      inputFileName   -- the name of the file to read\n     *      outputFileName  -- the name of the file to write \n     *                         the results\n     *  (If only one argument is supplied, the output is to System.out)\n     *  \n     *\/\n    public static void main(String&#x5B;] args)\n    {\n        try\n        {\n            BufferedReader reader = new BufferedReader( \n                                            new CommentFilterReader(\n                                                new FileReader(\n                                                    args&#x5B;0])));\n            PrintWriter writer = null;\n            if (args.length &gt; 1)\n            {\n                writer = new PrintWriter( \n                            new BufferedWriter(\n                                new FileWriter(args&#x5B;1])));\n            }\n            else\n            {\n                writer = new PrintWriter(\n                            new OutputStreamWriter(\n                                System.out));\n            }\n            \n            while (true)\n            {\n                String line = reader.readLine();\n                if (line == null)\n                    break;\n                writer.println(line);\n            }\n            reader.close();\n            writer.close();\n        }\n        catch (IOException e)\n        {\n            e.printStackTrace();\n        }\n    }\n}\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Note that I extended this class from&nbsp;<code>PushbackReader<\/code>, because I needed the ability to do a one-character read-ahead to determine whether a &#8216;<code><strong>\/<\/strong><\/code>&#8216; character was the start of a comment or not.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">When this program is applied to its own source, it outputs the following:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">public class CommentFilterReader extends PushbackReader\n{\n    public CommentFilterReader(Reader in)\n    {\n        super(in);\n    }\n    \n     \n    public int read() \n        throws IOException\n    {\n        int c = readNextNonCommentChar();\n        return c;\n    }\n    \n     \n    private int readNextNonCommentChar() \n        throws IOException\n    {\n        int c = super.read();\n        if (c == '\/')\n        {\n            \n            int lookAheadChar = super.read();\n            switch (lookAheadChar)\n            {\n                case '*':\n                    \n                    \n                    while (true)\n                    {\n                        c = super.read();\n                        if (c == -1)\n                            break;      \n                        if (c == '*')\n                        {\n                            lookAheadChar = super.read();\n                            if (lookAheadChar == '\/')\n                            {\n                                c = ' '; \n                                break;  \n                            }\n                        }\n                    }\n                    break;\n                        \n                case '\/':\n                    \n                    \n                    while (true)\n                    {\n                        c = super.read();\n                        if (c == -1)\n                            break;      \n                        if (c == '\\n')\n                        {\n                            \n                            \n                            break;\n                        }\n                    }\n                    break;\n                        \n                default:\n                    \n                    \n                    unread(lookAheadChar);\n                    break;\n            }\n        }\n        \n        \n        \n        return c;\n    }\n    \n     \n    public int read(char[] cbuf, int off, int len) \n        throws IOException\n    {\n        int count = 0;\n        \n        \n        for (int avail = cbuf.length - off; \n           len &gt; 0 &amp;&amp; avail &gt; 0; \n           len--, avail--)\n        {\n            int c = readNextNonCommentChar();\n            if (c == -1)\n            {\n                \n                \n                \n                \n                if (count == 0)\n                    count = -1;\n                break;\n            }\n            \n            \n            cbuf[off++] = (char)c;\n            count++;\n        }\n        \n        return count;\n    }\n    \n     \n    public long skip(long n) \n        throws IOException\n    {\n        long count = 0;\n        for (count = 0; count &lt; n; count++)\n        {\n            int c = readNextNonCommentChar();\n            if (c == -1)\n                break;\n        }\n        \n        return count;\n    }\n\n\n     \n    public static void main(String[] args)\n    {\n        try\n        {\n            BufferedReader reader = new BufferedReader( \n                                            new CommentFilterReader(\n                                                new FileReader(\n                                                    args[0])));\n            PrintWriter writer = null;\n            if (args.length &gt; 1)\n            {\n                writer = new PrintWriter( \n                            new BufferedWriter(\n                                new FileWriter(args[1])));\n            }\n            else\n            {\n                writer = new PrintWriter(\n                            new OutputStreamWriter(\n                                System.out));\n            }\n            \n            while (true)\n            {\n                String line = reader.readLine();\n                if (line == null)\n                    break;\n                writer.println(line);\n            }\n            reader.close();\n            writer.close();\n        }\n        catch (IOException e)\n        {\n            e.printStackTrace();\n        }\n    }\n}<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Here&#8217;s an example of how you might write a filter stream &#8212; here, a FilterReader. This one filters in such a way that it causes Java-style comments to be stripped from the stream as it passes through the filter: Note that I extended this class from&nbsp;PushbackReader, because I needed the ability to do a one-character [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"parent":67,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_eb_attr":"","ocean_front_end_style_editor":"no","ocean_post_layout":"left-sidebar","ocean_both_sidebars_style":"","ocean_both_sidebars_content_width":0,"ocean_both_sidebars_sidebars_width":0,"ocean_sidebar":"ocs-course-topics-sidebar","ocean_second_sidebar":"0","ocean_disable_margins":"enable","ocean_add_body_class":"","ocean_shortcode_before_top_bar":"","ocean_shortcode_after_top_bar":"","ocean_shortcode_before_header":"","ocean_shortcode_after_header":"","ocean_has_shortcode":"","ocean_shortcode_after_title":"","ocean_shortcode_before_footer_widgets":"","ocean_shortcode_after_footer_widgets":"","ocean_shortcode_before_footer_bottom":"","ocean_shortcode_after_footer_bottom":"","ocean_display_top_bar":"default","ocean_display_header":"default","ocean_header_style":"","ocean_center_header_left_menu":"0","ocean_custom_header_template":"0","ocean_custom_logo":0,"ocean_custom_retina_logo":0,"ocean_custom_logo_max_width":0,"ocean_custom_logo_tablet_max_width":0,"ocean_custom_logo_mobile_max_width":0,"ocean_custom_logo_max_height":0,"ocean_custom_logo_tablet_max_height":0,"ocean_custom_logo_mobile_max_height":0,"ocean_header_custom_menu":"0","ocean_menu_typo_font_family":"0","ocean_menu_typo_font_subset":"","ocean_menu_typo_font_size":0,"ocean_menu_typo_font_size_tablet":0,"ocean_menu_typo_font_size_mobile":0,"ocean_menu_typo_font_size_unit":"px","ocean_menu_typo_font_weight":"","ocean_menu_typo_font_weight_tablet":"","ocean_menu_typo_font_weight_mobile":"","ocean_menu_typo_transform":"","ocean_menu_typo_transform_tablet":"","ocean_menu_typo_transform_mobile":"","ocean_menu_typo_line_height":0,"ocean_menu_typo_line_height_tablet":0,"ocean_menu_typo_line_height_mobile":0,"ocean_menu_typo_line_height_unit":"","ocean_menu_typo_spacing":0,"ocean_menu_typo_spacing_tablet":0,"ocean_menu_typo_spacing_mobile":0,"ocean_menu_typo_spacing_unit":"","ocean_menu_link_color":"","ocean_menu_link_color_hover":"","ocean_menu_link_color_active":"","ocean_menu_link_background":"","ocean_menu_link_hover_background":"","ocean_menu_link_active_background":"","ocean_menu_social_links_bg":"","ocean_menu_social_hover_links_bg":"","ocean_menu_social_links_color":"","ocean_menu_social_hover_links_color":"","ocean_disable_title":"default","ocean_disable_heading":"default","ocean_post_title":"","ocean_post_subheading":"","ocean_post_title_style":"","ocean_post_title_background_color":"","ocean_post_title_background":0,"ocean_post_title_bg_image_position":"","ocean_post_title_bg_image_attachment":"","ocean_post_title_bg_image_repeat":"","ocean_post_title_bg_image_size":"","ocean_post_title_height":0,"ocean_post_title_bg_overlay":0.5,"ocean_post_title_bg_overlay_color":"","ocean_disable_breadcrumbs":"default","ocean_breadcrumbs_color":"","ocean_breadcrumbs_separator_color":"","ocean_breadcrumbs_links_color":"","ocean_breadcrumbs_links_hover_color":"","ocean_display_footer_widgets":"default","ocean_display_footer_bottom":"default","ocean_custom_footer_template":"0","footnotes":""},"class_list":["post-131","page","type-page","status-publish","hentry","entry"],"_links":{"self":[{"href":"https:\/\/bhiggs.x10hosting.com\/HighOctaneJava\/wp-json\/wp\/v2\/pages\/131","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/bhiggs.x10hosting.com\/HighOctaneJava\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/bhiggs.x10hosting.com\/HighOctaneJava\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/bhiggs.x10hosting.com\/HighOctaneJava\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/bhiggs.x10hosting.com\/HighOctaneJava\/wp-json\/wp\/v2\/comments?post=131"}],"version-history":[{"count":2,"href":"https:\/\/bhiggs.x10hosting.com\/HighOctaneJava\/wp-json\/wp\/v2\/pages\/131\/revisions"}],"predecessor-version":[{"id":135,"href":"https:\/\/bhiggs.x10hosting.com\/HighOctaneJava\/wp-json\/wp\/v2\/pages\/131\/revisions\/135"}],"up":[{"embeddable":true,"href":"https:\/\/bhiggs.x10hosting.com\/HighOctaneJava\/wp-json\/wp\/v2\/pages\/67"}],"wp:attachment":[{"href":"https:\/\/bhiggs.x10hosting.com\/HighOctaneJava\/wp-json\/wp\/v2\/media?parent=131"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}