diff --git a/src/main/java/net/sourceforge/plantuml/servlet/CheckSyntaxServlet.java b/src/main/java/net/sourceforge/plantuml/servlet/CheckSyntaxServlet.java index abcfc44..0e3c77a 100644 --- a/src/main/java/net/sourceforge/plantuml/servlet/CheckSyntaxServlet.java +++ b/src/main/java/net/sourceforge/plantuml/servlet/CheckSyntaxServlet.java @@ -34,6 +34,7 @@ import jakarta.servlet.http.HttpServletResponse; import net.sourceforge.plantuml.FileFormat; import net.sourceforge.plantuml.servlet.utility.UmlExtractor; +import net.sourceforge.plantuml.servlet.utility.UrlDataExtractor; /** * Check servlet of the webapp. @@ -46,7 +47,8 @@ public class CheckSyntaxServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // build the UML source from the compressed request parameter - String uml = UmlExtractor.getUmlSource(getSource(request.getRequestURI())); + final String url = request.getRequestURI(); + final String uml = UmlExtractor.getUmlSource(UrlDataExtractor.getEncodedDiagram(url, "")); // generate the response DiagramResponse dr = new DiagramResponse(response, getOutputFormat(), request); @@ -55,23 +57,6 @@ public class CheckSyntaxServlet extends HttpServlet { } catch (IIOException e) { // Browser has closed the connection, do nothing } - dr = null; - } - - /** - * Extract UML source from URI. - * - * @param uri the complete URI as returned by `request.getRequestURI()` - * - * @return the encoded UML text - */ - public String getSource(String uri) { - String[] result = uri.split("/check/", 2); - if (result.length != 2) { - return ""; - } else { - return result[1]; - } } /** diff --git a/src/main/java/net/sourceforge/plantuml/servlet/DiagramResponse.java b/src/main/java/net/sourceforge/plantuml/servlet/DiagramResponse.java index 955c1c5..01c5b60 100644 --- a/src/main/java/net/sourceforge/plantuml/servlet/DiagramResponse.java +++ b/src/main/java/net/sourceforge/plantuml/servlet/DiagramResponse.java @@ -166,10 +166,14 @@ public class DiagramResponse { * Produce and send the image map of the uml diagram in HTML format. * * @param uml textual UML diagram source + * @param idx diagram index of {@code uml} to send * * @throws IOException if an input or output exception occurred */ - public void sendMap(String uml) throws IOException { + public void sendMap(String uml, int idx) throws IOException { + if (idx < 0) { + idx = 0; + } response.setContentType(getContentType()); SourceStringReader reader = new SourceStringReader(uml); final BlockUml blockUml = reader.getBlocks().get(0); @@ -177,7 +181,7 @@ public class DiagramResponse { addHeaderForCache(blockUml); } final Diagram diagram = blockUml.getDiagram(); - ImageData map = diagram.exportDiagram(new NullOutputStream(), 0, + ImageData map = diagram.exportDiagram(new NullOutputStream(), idx, new FileFormatOption(FileFormat.PNG, false)); if (map.containsCMapData()) { PrintWriter httpOut = response.getWriter(); @@ -237,7 +241,7 @@ public class DiagramResponse { * * @param response http response */ - public static void addHeaders(HttpServletResponse response) { + private static void addHeaders(HttpServletResponse response) { response.addHeader("X-Powered-By", POWERED_BY); response.addHeader("X-Patreon", "Support us on https://plantuml.com/patreon"); response.addHeader("X-Donate", "https://plantuml.com/paypal"); diff --git a/src/main/java/net/sourceforge/plantuml/servlet/MapServlet.java b/src/main/java/net/sourceforge/plantuml/servlet/MapServlet.java index d647495..923af08 100644 --- a/src/main/java/net/sourceforge/plantuml/servlet/MapServlet.java +++ b/src/main/java/net/sourceforge/plantuml/servlet/MapServlet.java @@ -34,6 +34,7 @@ import jakarta.servlet.http.HttpServletResponse; import net.sourceforge.plantuml.FileFormat; import net.sourceforge.plantuml.servlet.utility.UmlExtractor; +import net.sourceforge.plantuml.servlet.utility.UrlDataExtractor; /** * MAP servlet of the webapp. @@ -46,32 +47,17 @@ public class MapServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // build the UML source from the compressed request parameter - String uml = UmlExtractor.getUmlSource(getSource(request.getRequestURI())); + final String url = request.getRequestURI(); + final String uml = UmlExtractor.getUmlSource(UrlDataExtractor.getEncodedDiagram(url, "")); + final int idx = UrlDataExtractor.getIndex(url, 0); // generate the response DiagramResponse dr = new DiagramResponse(response, getOutputFormat(), request); try { - dr.sendMap(uml); + dr.sendMap(uml, idx); } catch (IIOException e) { // Browser has closed the connection, do nothing } - dr = null; - } - - /** - * Extract UML source from URI. - * - * @param uri the complete URI as returned by `request.getRequestURI()` - * - * @return the encoded UML text - */ - public String getSource(String uri) { - String[] result = uri.split("/map/", 2); - if (result.length != 2) { - return ""; - } else { - return result[1]; - } } /** diff --git a/src/main/java/net/sourceforge/plantuml/servlet/PlantUmlServlet.java b/src/main/java/net/sourceforge/plantuml/servlet/PlantUmlServlet.java index a4206fb..60164c3 100644 --- a/src/main/java/net/sourceforge/plantuml/servlet/PlantUmlServlet.java +++ b/src/main/java/net/sourceforge/plantuml/servlet/PlantUmlServlet.java @@ -45,6 +45,7 @@ import net.sourceforge.plantuml.code.TranscoderUtil; import net.sourceforge.plantuml.png.MetadataTag; import net.sourceforge.plantuml.servlet.utility.Configuration; import net.sourceforge.plantuml.servlet.utility.UmlExtractor; +import net.sourceforge.plantuml.servlet.utility.UrlDataExtractor; /** * Original idea from Achim Abeling for Confluence macro. @@ -69,10 +70,6 @@ public class PlantUmlServlet extends HttpServlet { * Regex pattern to fetch last part of the URL. */ private static final Pattern URL_PATTERN = Pattern.compile("^.*[^a-zA-Z0-9\\-\\_]([a-zA-Z0-9\\-\\_]+)"); - /** - * Regex pattern to fetch encoded uml text from an "uml" URL. - */ - private static final Pattern RECOVER_UML_PATTERN = Pattern.compile("/uml/(.*)"); static { OptionFlags.ALLOW_INCLUDE = false; @@ -94,8 +91,11 @@ public class PlantUmlServlet extends HttpServlet { return; } + // diagram index to render + final int idx = UrlDataExtractor.getIndex(request.getRequestURI()); + // forward to index.jsp - prepareRequestForDispatch(request, text); + prepareRequestForDispatch(request, text, idx); final RequestDispatcher dispatcher = request.getRequestDispatcher("/index.jsp"); dispatcher.forward(request, response); } @@ -107,6 +107,9 @@ public class PlantUmlServlet extends HttpServlet { ) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); + // diagram index to render + final int idx = UrlDataExtractor.getIndex(request.getRequestURI()); + // encoded diagram source String encoded; try { @@ -117,7 +120,7 @@ public class PlantUmlServlet extends HttpServlet { e.printStackTrace(); } - redirectNow(request, response, encoded); + redirectNow(request, response, encoded, idx); } /** @@ -174,15 +177,16 @@ public class PlantUmlServlet extends HttpServlet { * @throws IOException if an input or output exception occurred */ private String getTextFromUrl(HttpServletRequest request) throws IOException { - final Matcher recoverUml = RECOVER_UML_PATTERN.matcher( - request.getRequestURI().substring(request.getContextPath().length()) - ); - // the URL form has been submitted - if (recoverUml.matches()) { - final String data = recoverUml.group(1); - return getTranscoder().decode(data); + // textual diagram source from request URI + String url = request.getRequestURI(); + if (url.contains("/uml/") && !url.endsWith("/uml/")) { + final String encoded = UrlDataExtractor.getEncodedDiagram(request.getRequestURI(), ""); + if (!encoded.isEmpty()) { + return getTranscoder().decode(encoded); + } } - String url = request.getParameter("url"); + // textual diagram source from "url" parameter + url = request.getParameter("url"); if (url != null && !url.trim().isEmpty()) { // Catch the last part of the URL if necessary final Matcher matcher = URL_PATTERN.matcher(url); @@ -203,11 +207,12 @@ public class PlantUmlServlet extends HttpServlet { * * @throws IOException if an input or output exception occurred */ - private void prepareRequestForDispatch(HttpServletRequest request, String text) throws IOException { - // diagram sources + private void prepareRequestForDispatch(HttpServletRequest request, String text, int idx) throws IOException { final String encoded = getTranscoder().encode(text); + final String index = (idx < 0) ? "" : idx + "/"; + // diagram sources request.setAttribute("decoded", text); - request.setAttribute("encoded", encoded); + request.setAttribute("index", idx); // properties request.setAttribute("showSocialButtons", Configuration.get("SHOW_SOCIAL_BUTTONS")); request.setAttribute("showGithubRibbon", Configuration.get("SHOW_GITHUB_RIBBON")); @@ -217,10 +222,10 @@ public class PlantUmlServlet extends HttpServlet { // image URLs final boolean hasImg = !text.isEmpty(); request.setAttribute("hasImg", hasImg); - request.setAttribute("imgurl", hostpath + "/png/" + encoded); - request.setAttribute("svgurl", hostpath + "/svg/" + encoded); - request.setAttribute("txturl", hostpath + "/txt/" + encoded); - request.setAttribute("mapurl", hostpath + "/map/" + encoded); + request.setAttribute("imgurl", hostpath + "/png/" + index + encoded); + request.setAttribute("svgurl", hostpath + "/svg/" + index + encoded); + request.setAttribute("txturl", hostpath + "/txt/" + index + encoded); + request.setAttribute("mapurl", hostpath + "/map/" + index + encoded); // map for diagram source if necessary final boolean hasMap = PlantumlUtils.hasCMapData(text); request.setAttribute("hasMap", hasMap); @@ -276,7 +281,32 @@ public class PlantUmlServlet extends HttpServlet { HttpServletResponse response, String encoded ) throws IOException { - final String result = request.getContextPath() + "/uml/" + encoded; + redirectNow(request, response, encoded, null); + } + + /** + * Send redirect response to encoded uml text. + * + * @param request http request + * @param response http response + * @param encoded encoded uml text + * @param index diagram index + * + * @throws IOException if an input or output exception occurred + */ + private void redirectNow( + HttpServletRequest request, + HttpServletResponse response, + String encoded, + Integer index + ) throws IOException { + final String result; + if (index == null || index < 0) { + result = request.getContextPath() + "/uml/" + encoded; + } else { + result = request.getContextPath() + "/uml/" + index + "/" + encoded; + } + response.sendRedirect(result); } diff --git a/src/main/java/net/sourceforge/plantuml/servlet/UmlDiagramService.java b/src/main/java/net/sourceforge/plantuml/servlet/UmlDiagramService.java index d75179a..167bf91 100644 --- a/src/main/java/net/sourceforge/plantuml/servlet/UmlDiagramService.java +++ b/src/main/java/net/sourceforge/plantuml/servlet/UmlDiagramService.java @@ -25,8 +25,6 @@ package net.sourceforge.plantuml.servlet; import java.io.BufferedReader; import java.io.IOException; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import javax.imageio.IIOException; @@ -34,9 +32,11 @@ import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; + import net.sourceforge.plantuml.FileFormat; import net.sourceforge.plantuml.OptionFlags; import net.sourceforge.plantuml.servlet.utility.UmlExtractor; +import net.sourceforge.plantuml.servlet.utility.UrlDataExtractor; /** * Common service servlet to produce diagram from compressed UML source contained in the end part of the requested URI. @@ -44,11 +44,6 @@ import net.sourceforge.plantuml.servlet.utility.UmlExtractor; @SuppressWarnings("SERIAL") public abstract class UmlDiagramService extends HttpServlet { - /** - * Regex pattern to fetch encoded uml text from an URL. - */ - private static final Pattern RECOVER_UML_PATTERN = Pattern.compile("/\\w+/(\\d+/)?(.*)"); - static { OptionFlags.ALLOW_INCLUDE = false; if ("true".equalsIgnoreCase(System.getenv("ALLOW_PLANTUML_INCLUDE"))) { @@ -58,12 +53,14 @@ public abstract class UmlDiagramService extends HttpServlet { @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + final String url = request.getRequestURI(); + final String encoded = UrlDataExtractor.getEncodedDiagram(url, ""); + final int idx = UrlDataExtractor.getIndex(url, 0); + // build the UML source from the compressed request parameter - final String[] sourceAndIdx = getSourceAndIdx(request); - final int idx = Integer.parseInt(sourceAndIdx[1]); final String uml; try { - uml = UmlExtractor.getUmlSource(sourceAndIdx[0]); + uml = UmlExtractor.getUmlSource(encoded); } catch (Exception e) { e.printStackTrace(); response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Bad Request"); @@ -75,18 +72,15 @@ public abstract class UmlDiagramService extends HttpServlet { @Override public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { - // build the UML source from the compressed request parameter - final String[] sourceAndIdx = getSourceAndIdx(request); - final int idx = Integer.parseInt(sourceAndIdx[1]); + final int idx = UrlDataExtractor.getIndex(request.getRequestURI(), 0); + // read textual diagram source from request body final StringBuilder uml = new StringBuilder(); - final BufferedReader in = request.getReader(); - while (true) { - final String line = in.readLine(); - if (line == null) { - break; + try (BufferedReader in = request.getReader()) { + String line; + while ((line = in.readLine()) != null) { + uml.append(line).append('\n'); } - uml.append(line).append('\n'); } doDiagramResponse(request, response, uml.toString(), idx); @@ -118,33 +112,6 @@ public abstract class UmlDiagramService extends HttpServlet { } } - /** - * Extracts the UML source text and its index from the HTTP request. - * - * @param request http request - * - * @return the UML source text and its index - */ - public final String[] getSourceAndIdx(HttpServletRequest request) { - final Matcher recoverUml = RECOVER_UML_PATTERN.matcher( - request.getRequestURI().substring( - request.getContextPath().length())); - // the URL form has been submitted - if (recoverUml.matches()) { - final String data = recoverUml.group(2); - if (data.length() >= 4) { - String idx = recoverUml.group(1); - if (idx == null) { - idx = "0"; - } else { - idx = idx.substring(0, idx.length() - 1); - } - return new String[]{data, idx }; - } - } - return new String[]{"", "0"}; - } - /** * Gives the wished output format of the diagram. This value is used by the DiagramResponse class. * diff --git a/src/main/java/net/sourceforge/plantuml/servlet/diagrams.txt b/src/main/java/net/sourceforge/plantuml/servlet/diagrams.txt index 34fa17a..dd7e69a 100644 --- a/src/main/java/net/sourceforge/plantuml/servlet/diagrams.txt +++ b/src/main/java/net/sourceforge/plantuml/servlet/diagrams.txt @@ -6,26 +6,33 @@ hide empty members hide empty methods hide empty fields abstract class UmlDiagramService { - public void doGet(HttpServletRequest rq, HttpServletResponse rsp) - abstract public String getSource( String uri) - abstract FileFormat getOutputFormat() + + doGet(request: HttpServletRequest, response: HttpServletResponse) : void + + doPost(request: HttpServletRequest, response: HttpServletResponse) : void + + {abstract} getOutputFormat() : FileFormat } class DiagramResponse { - DiagramResponse( HttpServletResponse r, FileFormat f) - void sendDiagram( String uml) - void sendMap( String uml) + + DiagramResponse(res: HttpServletResponse, fmt: FileFormat, req: HttpServletRequest) + + sendDiagram(uml: String, idx: int) : void + + sendMap(uml: String, idx: int) : void + + sendCheck(uml: String) : void } -abstract HttpServlet <|-- UmlDiagramService -abstract HttpServlet <|-- MapServlet -abstract HttpServlet <|-- ProxyServlet -UmlDiagramService <|-- PngServlet -UmlDiagramService <|-- SvgServlet +HttpServlet <|-- CheckSyntaxServlet +HttpServlet <|-- LanguageServlet +HttpServlet <|-- MapServlet +HttpServlet <|-- PlantUmlServlet +HttpServlet <|-- ProxyServlet +HttpServlet <|-- OldProxyServlet +HttpServlet <|-- UmlDiagramService +UmlDiagramService <|-- AsciiServlet +UmlDiagramService <|-- Base64Servlet UmlDiagramService <|-- EpsServlet UmlDiagramService <|-- EpsTextServlet -UmlDiagramService <|-- AsciiServlet -UmlDiagramService o- DiagramResponse -MapServlet o-- DiagramResponse -ProxyServlet o-- DiagramResponse +UmlDiagramService <|-- ImgServlet +UmlDiagramService <|-- SvgServlet +UmlDiagramService o-- DiagramResponse +DiagramResponse --o CheckSyntaxServlet +DiagramResponse --o MapServlet +DiagramResponse --o ProxyServlet @enduml ## Sequence diagram ## @@ -33,14 +40,17 @@ ProxyServlet o-- DiagramResponse @startuml title Generation of a PNG image illustrated -PngServlet -> PngServlet : getSource() -PngServlet -> UmlExtractor : getUmlSource() -UmlExtractor --> PngServlet -PngServlet -> PngServlet : getOutputFormat() -PngServlet -> DiagramResponse : <> -PngServlet -> DiagramResponse : sendDiagram() +ImgServlet -> UrlDataExtractor : getEncodedDiagram() +UrlDataExtractor --> ImgServlet : encoded +ImgServlet -> UrlDataExtractor : getIndex() +UrlDataExtractor --> ImgServlet : index +ImgServlet -> UmlExtractor : getUmlSource() +UmlExtractor --> ImgServlet : decoded +ImgServlet -> ImgServlet : getOutputFormat() +ImgServlet -> "dr:DiagramResponse" as dr ** : <> +ImgServlet -> dr : sendDiagram() participant "PlantUML library" as Lib #99FF99 -DiagramResponse -> Lib : generateImage() -Lib --> DiagramResponse -DiagramResponse --> PngServlet -@enduml \ No newline at end of file +dr -> Lib : generateImage() +Lib --> dr +dr --> ImgServlet +@enduml diff --git a/src/main/java/net/sourceforge/plantuml/servlet/utility/UrlDataExtractor.java b/src/main/java/net/sourceforge/plantuml/servlet/utility/UrlDataExtractor.java new file mode 100644 index 0000000..2f2add5 --- /dev/null +++ b/src/main/java/net/sourceforge/plantuml/servlet/utility/UrlDataExtractor.java @@ -0,0 +1,102 @@ +/* ======================================================================== + * PlantUML : a free UML diagram generator + * ======================================================================== + * + * Project Info: https://plantuml.com + * + * This file is part of PlantUML. + * + * PlantUML 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. + * + * PlantUML 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 Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + */ +package net.sourceforge.plantuml.servlet.utility; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Utility class to extract the index and diagram source from an URL, e.g., returned by `request.getRequestURI()`. + */ +public abstract class UrlDataExtractor { + + /** + * URL regex pattern to easily extract index and encoded diagram. + */ + private static final Pattern URL_PATTERN = Pattern.compile( + "/\\w+(?:/(?\\d+))?(?:/(?[^/]+))?/?$" + ); + + /** + * Get diagram index from URL. + * + * @param url URL to analyse, e.g., returned by `request.getRequestURI()` + * + * @return if exists diagram index; otherwise -1 + */ + public static int getIndex(final String url) { + return getIndex(url, -1); + } + + /** + * Get diagram index from URL. + * + * @param url URL to analyse, e.g., returned by `request.getRequestURI()` + * @param fallback fallback index if no index exists in {@code url} + * + * @return if exists diagram index; otherwise {@code fallback} + */ + public static int getIndex(final String url, final int fallback) { + final Matcher matcher = URL_PATTERN.matcher(url); + if (!matcher.find()) { + return fallback; + } + String idx = matcher.group("idx"); + if (idx == null) { + return fallback; + } + return Integer.parseInt(idx); + } + + /** + * Get encoded diagram source from URL. + * + * @param url URL to analyse, e.g., returned by `request.getRequestURI()` + * + * @return if exists diagram index; otherwise `null` + */ + public static String getEncodedDiagram(final String url) { + return getEncodedDiagram(url, null); + } + + /** + * Get encoded diagram source from URL. + * + * @param url URL to analyse, e.g., returned by `request.getRequestURI()` + * @param fallback fallback if no encoded diagram source exists in {@code url} + * + * @return if exists diagram index; otherwise {@code fallback} + */ + public static String getEncodedDiagram(final String url, final String fallback) { + final Matcher matcher = URL_PATTERN.matcher(url); + if (!matcher.find()) { + return fallback; + } + String encoded = matcher.group("encoded"); + if (encoded == null) { + return fallback; + } + return encoded; + } +} diff --git a/src/main/webapp/index.jsp b/src/main/webapp/index.jsp index 25d16db..42711bc 100644 --- a/src/main/webapp/index.jsp +++ b/src/main/webapp/index.jsp @@ -3,7 +3,6 @@ <% // diagram sources String decoded = request.getAttribute("decoded").toString(); - String encoded = request.getAttribute("encoded").toString(); // properties boolean showSocialButtons = (boolean)request.getAttribute("showSocialButtons"); boolean showGithubRibbon = (boolean)request.getAttribute("showGithubRibbon"); diff --git a/src/test/java/net/sourceforge/plantuml/servlet/TestForm.java b/src/test/java/net/sourceforge/plantuml/servlet/TestForm.java index 42cf7a6..894ecd1 100644 --- a/src/test/java/net/sourceforge/plantuml/servlet/TestForm.java +++ b/src/test/java/net/sourceforge/plantuml/servlet/TestForm.java @@ -216,4 +216,88 @@ public class TestForm extends WebappTestCase { } } + /** + * Verifies that an multipage diagram renders correct given index. + * + * Bob -> Alice : hello + * newpage + * Bob <- Alice : hello + * Bob -> Alice : let's talk + * Bob <- Alice : better not + * Bob -> Alice : <&rain> bye + * newpage + * Bob <- Alice : bye + */ + public void testIndexPage() throws IOException { + try (final WebClient webClient = new WebClient()) { + HtmlPage page = webClient.getPage( + getServerUrl() + "/uml/1/" + + "SyfFKj2rKt3CoKnELR1Io4ZDoSddoaijBqXCJ-Lo0ahQwA99Eg7go4ajKIzMA4dCoKPNdfHQKf9Qf92NNuAknqQjA34ppquXgJ8Lbrr0AG00" + ); + // Analyze response + List forms = page.getForms(); + assertEquals(2, forms.size()); + // Ensure the Text field is correct + String text = ((HtmlTextArea)(forms.get(0).getFirstByXPath("//textarea[contains(@name, 'text')]"))).getTextContent(); + assertEquals( + "@startuml\nBob -> Alice : hello\nnewpage\nBob <- Alice : hello\nBob -> Alice : let's talk\nBob <- Alice : better not\nBob -> Alice : <&rain> bye\nnewpage\nBob <- Alice : bye\n@enduml", + text + ); + // Ensure the URL field is correct + HtmlInput url = forms.get(1).getInputByName("url"); + assertNotNull(url); + assertTrue(url.getAttribute("value").endsWith("/png/1/SyfFKj2rKt3CoKnELR1Io4ZDoSddoaijBqXCJ-Lo0ahQwA99Eg7go4ajKIzMA4dCoKPNdfHQKf9Qf92NNuAknqQjA34ppquXgJ8Lbrr0AG00")); + // Ensure the generated image is present + HtmlImage img = page.getFirstByXPath("//img[contains(@alt, 'PlantUML diagram')]"); + int height = img.getImageReader().getHeight(0); + assertNotEquals(0, height); // 222 + assertNotEquals(0, img.getImageReader().getWidth(0)); // 152 + // Ensure the correct index was generated + assertTrue(height > 200); // 222 + assertTrue(height < 250); // 222 + } + } + + /** + * Verifies that an multipage diagram renders correct even if no index is specified. + * + * Bob -> Alice : hello + * newpage + * Bob <- Alice : hello + * Bob -> Alice : let's talk + * Bob <- Alice : better not + * Bob -> Alice : <&rain> bye + * newpage + * Bob <- Alice : bye + */ + public void testIndexPageWithNoDefinedIndex() throws IOException { + try (final WebClient webClient = new WebClient()) { + HtmlPage page = webClient.getPage( + getServerUrl() + "/uml/" + + "SyfFKj2rKt3CoKnELR1Io4ZDoSddoaijBqXCJ-Lo0ahQwA99Eg7go4ajKIzMA4dCoKPNdfHQKf9Qf92NNuAknqQjA34ppquXgJ8Lbrr0AG00" + ); + // Analyze response + List forms = page.getForms(); + assertEquals(2, forms.size()); + // Ensure the Text field is correct + String text = ((HtmlTextArea)(forms.get(0).getFirstByXPath("//textarea[contains(@name, 'text')]"))).getTextContent(); + assertEquals( + "@startuml\nBob -> Alice : hello\nnewpage\nBob <- Alice : hello\nBob -> Alice : let's talk\nBob <- Alice : better not\nBob -> Alice : <&rain> bye\nnewpage\nBob <- Alice : bye\n@enduml", + text + ); + // Ensure the URL field is correct + HtmlInput url = forms.get(1).getInputByName("url"); + assertNotNull(url); + assertTrue(url.getAttribute("value").endsWith("/png/SyfFKj2rKt3CoKnELR1Io4ZDoSddoaijBqXCJ-Lo0ahQwA99Eg7go4ajKIzMA4dCoKPNdfHQKf9Qf92NNuAknqQjA34ppquXgJ8Lbrr0AG00")); + // Ensure the generated image is present + HtmlImage img = page.getFirstByXPath("//img[contains(@alt, 'PlantUML diagram')]"); + int height = img.getImageReader().getHeight(0); + assertNotEquals(0, height); // 132 + assertNotEquals(0, img.getImageReader().getWidth(0)); // 152 + // Ensure the correct index was generated + assertTrue(height > 100); // 132 + assertTrue(height < 150); // 132 + } + } + } diff --git a/src/test/java/net/sourceforge/plantuml/servlet/TestMultipageUml.java b/src/test/java/net/sourceforge/plantuml/servlet/TestMultipageUml.java new file mode 100644 index 0000000..2b33c3b --- /dev/null +++ b/src/test/java/net/sourceforge/plantuml/servlet/TestMultipageUml.java @@ -0,0 +1,199 @@ +package net.sourceforge.plantuml.servlet; + +import static org.junit.Assert.assertNotEquals; + +import java.io.IOException; +import java.net.URL; +import java.net.URLConnection; + + +public class TestMultipageUml extends WebappTestCase { + + /** + * Encoded/compressed diagram source to text multipage uml diagrams. + * + * Bob -> Alice : hello + * newpage + * Bob <- Alice : hello + * Bob -> Alice : let's talk [[tel:0123456789]] + * Bob <- Alice : better not + * Bob -> Alice : <&rain> bye + * newpage + * Bob <- Alice : bye + */ + private static final String ENCODED = "SoWkIImgAStDuNBAJrBGjLDmpCbCJbMmKiX8pSd9vyfBBIz8J4y5IzheeagYwyX9BL4lLYX9pCbMY8ukISsnCZ0qCZOnDJEti8oDHJSXARMa9BL88I-_1DqO6xMYnCmyEuMaobGSreEb75BpKe3E1W00"; + + /** + * Verifies that an multipage diagram renders correct given index (PNG). + */ + public void testPngIndexPage() throws IOException { + final URL url = new URL(getServerUrl() + "/png/1/" + ENCODED); + final URLConnection conn = url.openConnection(); + // Analyze response + // Verifies the Content-Type header + assertEquals( + "Response content type is not PNG", + "image/png", + conn.getContentType().toLowerCase() + ); + // Get the image and verify its size + byte[] inMemoryImage = getContentAsBytes(conn); + int diagramLen = inMemoryImage.length; // 7525 + assertTrue(diagramLen > 6000); + assertTrue(diagramLen < 9000); + } + + /** + * Verifies that an multipage diagram renders correct even if no index is specified (PNG). + */ + public void testPngIndexPageNoIndex() throws IOException { + final URL url = new URL(getServerUrl() + "/png/" + ENCODED); + final URLConnection conn = url.openConnection(); + // Analyze response + // Verifies the Content-Type header + assertEquals( + "Response content type is not PNG", + "image/png", + conn.getContentType().toLowerCase() + ); + // Get the image and verify its size + byte[] inMemoryImage = getContentAsBytes(conn); + int diagramLen = inMemoryImage.length; // 4196 + assertTrue(diagramLen > 3000); + assertTrue(diagramLen < 5000); + } + + /** + * Verifies that an multipage diagram renders correct given index (SVG). + */ + public void testSvgIndexPage() throws IOException { + final URL url = new URL(getServerUrl() + "/svg/1/" + ENCODED); + final URLConnection conn = url.openConnection(); + // Analyze response + // Verifies the Content-Type header + assertEquals( + "Response content type is not SVG", + "image/svg+xml", + conn.getContentType().toLowerCase() + ); + // Get the content and verify its size + String diagram = getContentText(conn); + int diagramLen = diagram.length(); // 5834 + assertTrue(diagramLen > 5500); + assertTrue(diagramLen < 7000); + } + + /** + * Verifies that an multipage diagram renders correct even if no index is specified (SVG). + */ + public void testSvgIndexPageNoIndex() throws IOException { + final URL url = new URL(getServerUrl() + "/svg/" + ENCODED); + final URLConnection conn = url.openConnection(); + // Analyze response + // Verifies the Content-Type header + assertEquals( + "Response content type is not SVG", + "image/svg+xml", + conn.getContentType().toLowerCase() + ); + // Get the content and verify its size + String diagram = getContentText(conn); + int diagramLen = diagram.length(); // 2970 + assertTrue(diagramLen > 1500); + assertTrue(diagramLen < 4000); + } + + /** + * Verifies that an multipage diagram renders correct given index (AsciiArt). + */ + public void testAsciiArtIndexPage() throws IOException { + final URL url = new URL(getServerUrl() + "/txt/1/" + ENCODED); + final URLConnection conn = url.openConnection(); + // Analyze response + // Verifies the Content-Type header + assertEquals( + "Response content type is not TEXT PLAIN or UTF-8", + "text/plain;charset=utf-8", + conn.getContentType().toLowerCase() + ); + // Get the content and verify its size + String diagram = getContentText(conn); + int diagramLen = diagram.length(); + assertNotEquals(0, diagramLen); + // BUG/Missing Feature: plantuml renders always whole AsciiArt diagram + //assertTrue(diagramLen > ??); + //assertTrue(diagramLen < ??); + } + + /** + * Verifies that an multipage diagram renders correct even if no index is specified (AsciiArt). + */ + public void testAsciiArtIndexPageNoIndex() throws IOException { + final URL url = new URL(getServerUrl() + "/txt/" + ENCODED); + final URLConnection conn = url.openConnection(); + // Analyze response + // Verifies the Content-Type header + assertEquals( + "Response content type is not TEXT PLAIN or UTF-8", + "text/plain;charset=utf-8", + conn.getContentType().toLowerCase() + ); + // Get the content and verify its size + String diagram = getContentText(conn); + int diagramLen = diagram.length(); + assertNotEquals(0, diagramLen); + // BUG/Missing Feature: plantuml renders always whole AsciiArt diagram + //assertTrue(diagramLen > ??); + //assertTrue(diagramLen < ??); + } + + /** + * Verifies that an multipage diagram renders correct given index (Map). + */ + public void testMapIndexPage() throws IOException { + final URL url = new URL(getServerUrl() + "/map/1/" + ENCODED); + final URLConnection conn = url.openConnection(); + // Analyze response + // Verifies the Content-Type header + assertEquals( + "Response content type is not TEXT PLAIN or UTF-8", + "text/plain;charset=utf-8", + conn.getContentType().toLowerCase() + ); + // Get the data contained in the XML + String map = getContentText(url); + // map contains "tel:0123456789" + assertTrue( + "Response does not contain 'tel:0123456789'", + map.contains("tel:0123456789") + ); + // Verify shape: + // + // + // + assertTrue( + "Response doesn't match shape", + map.matches("^\n(\n)\n*$") + ); + } + + /** + * Verifies that an multipage diagram renders correct even if no index is specified (Map). + */ + public void testMapIndexPageNoIndex() throws IOException { + final URL url = new URL(getServerUrl() + "/map/" + ENCODED); + final URLConnection conn = url.openConnection(); + // Analyze response + // Verifies the Content-Type header + assertEquals( + "Response content type is not TEXT PLAIN or UTF-8", + "text/plain;charset=utf-8", + conn.getContentType().toLowerCase() + ); + // Get the data contained in the XML + String diagram = getContentText(conn); + int diagramLen = diagram.length(); + assertEquals(0, diagramLen); + } + +}