001    // License: GPL. For details, see LICENSE file.
002    package org.openstreetmap.josm.gui.help;
003    
004    import java.io.BufferedReader;
005    import java.io.IOException;
006    import java.io.InputStreamReader;
007    import java.net.HttpURLConnection;
008    import java.net.MalformedURLException;
009    import java.net.URL;
010    
011    import org.openstreetmap.josm.tools.WikiReader;
012    
013    /**
014     * Reads help content from the JOSM Wiki and prepares it for rendering in the internal
015     * help browser.
016     *
017     * The help content has to be <strong>filtered</strong> because only the main content <tt>&lt;div&gt;</tt>
018     * of a Wiki help page is displayed in the internal help browser.
019     *
020     * It also has to be <strong>transformed</strong> because the internal help browser required slightly
021     * different HTML than what is provided by the Wiki.
022     *
023     * @see WikiReader
024     */
025    public class HelpContentReader {
026    
027        /** the base url */
028        private String baseUrl;
029    
030        /**
031         * constructor
032         *
033         * @param baseUrl the base url of the JOSM help wiki, i.e. http://josm.openstreetmap.org
034         */
035        public HelpContentReader(String baseUrl) {
036            this.baseUrl = baseUrl;
037        }
038    
039        /**
040         * Fetches the content of a help topic from the JOSM wiki.
041         *
042         * @param helpTopicUrl  the absolute help topic URL
043         * @return the content, filtered and transformed for being displayed in the internal help browser
044         * @throws HelpContentReaderException thrown if problem occurs
045         * @throws MissingHelpContentException thrown if this helpTopicUrl doesn't point to an existing Wiki help page
046         */
047        public String fetchHelpTopicContent(String helpTopicUrl, boolean dotest) throws HelpContentReaderException {
048            URL url = null;
049            HttpURLConnection con = null;
050            BufferedReader in = null;
051            try {
052                url = new URL(helpTopicUrl);
053                con = (HttpURLConnection)url.openConnection();
054                con.connect();
055                in = new BufferedReader(new InputStreamReader(con.getInputStream(),"utf-8"));
056                return prepareHelpContent(in, dotest);
057            } catch(MalformedURLException e) {
058                throw new HelpContentReaderException(e);
059            } catch(IOException e) {
060                HelpContentReaderException ex = new HelpContentReaderException(e);
061                if (con != null) {
062                    try {
063                        ex.setResponseCode(con.getResponseCode());
064                    } catch(IOException e1) {
065                        // ignore
066                    }
067                }
068                throw ex;
069            } finally {
070                if (in != null) {
071                    try {
072                        in.close();
073                    } catch(IOException e) {
074                        // ignore
075                    }
076                }
077            }
078        }
079    
080        /**
081         * Reads help content from the input stream and prepares it to be rendered later
082         * in the internal help browser.
083         *
084         * Throws a {@link MissingHelpContentException} if the content read from the stream
085         * most likely represents a stub help page.
086         *
087         * @param in the input stream
088         * @return the content
089         * @throws HelpContentReaderException thrown if an exception occurs
090         * @throws MissingHelpContentException thrown, if the content read isn't a help page
091         */
092        protected String prepareHelpContent(BufferedReader in, boolean dotest) throws HelpContentReaderException {
093            boolean isInContent = false;
094            boolean isInTranslationsSideBar = false;
095            boolean isExistingHelpPage = false;
096            StringBuffer sball = new StringBuffer();
097            StringBuffer sb = new StringBuffer();
098            try {
099                for (String line = in.readLine(); line != null; line = in.readLine()) {
100                    sball.append(line);
101                    sball.append("\n");
102                    if (line.contains("<div id=\"searchable\">")) {
103                        isInContent = true;
104                    } else if (line.contains("<div class=\"wiki-toc trac-nav\"")) {
105                        isInTranslationsSideBar = true;
106                    } else if (line.contains("<div class=\"wikipage searchable\">")) {
107                        isInContent = true;
108                    } else if (line.contains("<div class=\"buttons\">")) {
109                        isInContent = false;
110                    } else if (line.contains("<h3>Attachments</h3>")) {
111                        isInContent = false;
112                    } else if (line.contains("<div id=\"attachments\">")) {
113                        isInContent = false;
114                    } else if (line.contains("<div class=\"trac-modifiedby\">")) {
115                        continue;
116                    } else if (line.contains("<input type=\"submit\" name=\"attachfilebutton\"")) {
117                        // heuristic: if we find a button for uploading images we are in an
118                        // existing pages. Otherwise this is probably the stub page for a not yet
119                        // existing help page
120                        isExistingHelpPage = true;
121                    }
122                    if (isInContent && !isInTranslationsSideBar) {
123                        // add a border="0" attribute to images, otherwise the internal help browser
124                        // will render a thick  border around images inside an <a> element
125                        //
126                        // Also make sure image URLs are absolute
127                        //
128                        line = line.replaceAll("<img ([^>]*)src=\"/", "<img border=\"0\" \\1src=\"" + baseUrl + "/").replaceAll("href=\"/",
129                                "href=\"" + baseUrl + "/").replaceAll(" />", ">");
130                        sb.append(line);
131                        sb.append("\n");
132                    } else if (isInTranslationsSideBar && line.contains("</div>")) {
133                        isInTranslationsSideBar = false;
134                    }
135                }
136            } catch(IOException e) {
137                throw new HelpContentReaderException(e);
138            }
139            if(!dotest && sb.length() == 0)
140                sb = sball;
141            else if (dotest && !isExistingHelpPage)
142                throw new MissingHelpContentException();
143            sb.insert(0, "<html>");
144            sb.append("<html>");
145            return sb.toString();
146        }
147    }