diff --git a/plugins/org.polarsys.capella.vp.requirements.design/META-INF/MANIFEST.MF b/plugins/org.polarsys.capella.vp.requirements.design/META-INF/MANIFEST.MF index 85c5b777..933b36ca 100644 --- a/plugins/org.polarsys.capella.vp.requirements.design/META-INF/MANIFEST.MF +++ b/plugins/org.polarsys.capella.vp.requirements.design/META-INF/MANIFEST.MF @@ -15,7 +15,8 @@ Require-Bundle: org.eclipse.ui, org.polarsys.capella.core.data.business.queries, org.eclipse.sirius.common, org.polarsys.capella.vp.requirements.ui.commands, - org.polarsys.kitalpha.vp.requirements.model.helpers + org.polarsys.kitalpha.vp.requirements.model.helpers, + org.jsoup;bundle-version="1.15.3" Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-17 Bundle-Localization: plugin diff --git a/plugins/org.polarsys.capella.vp.requirements.design/src/org/polarsys/capella/vp/requirements/design/service/CapellaRequirementsOpenJavaService.java b/plugins/org.polarsys.capella.vp.requirements.design/src/org/polarsys/capella/vp/requirements/design/service/CapellaRequirementsOpenJavaService.java index bec07783..23a41347 100644 --- a/plugins/org.polarsys.capella.vp.requirements.design/src/org/polarsys/capella/vp/requirements/design/service/CapellaRequirementsOpenJavaService.java +++ b/plugins/org.polarsys.capella.vp.requirements.design/src/org/polarsys/capella/vp/requirements/design/service/CapellaRequirementsOpenJavaService.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2016, 2019, 2019 THALES GLOBAL SERVICES. + * Copyright (c) 2016, 2024 THALES GLOBAL SERVICES. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -13,10 +13,12 @@ package org.polarsys.capella.vp.requirements.design.service; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Set; +import java.util.regex.Pattern; import java.util.stream.Collectors; import org.eclipse.emf.ecore.EAnnotation; @@ -30,11 +32,17 @@ import org.eclipse.sirius.diagram.DEdge; import org.eclipse.sirius.diagram.DSemanticDiagram; import org.eclipse.sirius.diagram.EdgeTarget; -import org.eclipse.sirius.diagram.model.business.internal.helper.ContentLayerHelper; import org.eclipse.sirius.diagram.description.EdgeMapping; -import org.eclipse.sirius.diagram.description.Layer; +import org.eclipse.sirius.diagram.description.Layer; +import org.eclipse.sirius.diagram.model.business.internal.helper.ContentLayerHelper; import org.eclipse.sirius.viewpoint.DSemanticDecorator; import org.eclipse.sirius.viewpoint.ViewpointPackage; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Node; +import org.jsoup.nodes.TextNode; +import org.jsoup.select.NodeTraversor; +import org.jsoup.select.NodeVisitor; import org.polarsys.capella.common.mdsofa.common.constant.ICommonConstants; import org.polarsys.capella.core.business.queries.IBusinessQuery; import org.polarsys.capella.core.business.queries.capellacore.BusinessQueriesProvider; @@ -338,7 +346,7 @@ public String getRequirementTitle(Requirement requirement) { .get(ReqVPCustomDataHelper.CUSTOM_DATA_KEY_FOR_REQ_VP_QUERIES_LABEL_LENGTH); } - return evaluateExpression(session, requirement, expression, maxLength); + return evaluateExpression(session, requirement, expression, maxLength, false); } /** @@ -360,7 +368,7 @@ public String getRequirementContent(Requirement requirement) { .get(ReqVPCustomDataHelper.CUSTOM_DATA_KEY_FOR_REQ_VP_QUERIES_CONTENT_LENGTH); } - return evaluateExpression(session, requirement, expression, maxLength); + return evaluateExpression(session, requirement, expression, maxLength, true); } /** @@ -370,9 +378,10 @@ public String getRequirementContent(Requirement requirement) { * @param requirement * @param expression * @param maxLength + * @param keepEnfOfLine * @return */ - protected String evaluateExpression(Session session, Requirement requirement, String expression, String maxLength) { + protected String evaluateExpression(Session session, Requirement requirement, String expression, String maxLength, boolean keepEnfOfLine) { try { if (session != null && expression != null) { IInterpreter interpreter = session.getInterpreter(); @@ -387,7 +396,7 @@ protected String evaluateExpression(Session session, Requirement requirement, St resultBuilder.append(value); } String evaluationResult = resultBuilder.toString(); - String sanytizedResult = LabelHelper.unescape(LabelHelper.transformHTMLToText(evaluationResult)); + String sanytizedResult = getTextFromHtml(evaluationResult, keepEnfOfLine); return reduceString(sanytizedResult, maxLength); } } @@ -418,6 +427,20 @@ protected String reduceString(String value, String maxLengthText) { return value; } + public static String getTextFromHtml(String html, boolean keepMultiline) { + if (html == null) { + return null; + } + + Html2TextVisitor visitor = new Html2TextVisitor(keepMultiline); + Document htmlDoc = Jsoup.parse(html); + NodeTraversor.traverse(visitor, htmlDoc); + + String text = visitor.toString(); + text = text.trim(); + return text; + } + /** * Find the source of the outgoing relation with a requirement. With this relation, the target is a requirement, the * source is capella element or diagram. @@ -552,4 +575,90 @@ public boolean isLinkedToDiagram(EObject requirement, EObject diagram) { } return false; } -} \ No newline at end of file + + private static class Html2TextVisitor implements NodeVisitor { + + private static final Pattern MULTI_BLANKS_PATTERN = Pattern.compile("\\p{javaWhitespace}+"); + + private final StringBuilder str = new StringBuilder(); + + private final boolean keepMultiline; + + public Html2TextVisitor(boolean keepMultiline) { + this.keepMultiline = keepMultiline; + } + + private void addNewLineIfNoneBefore() { + if (str.length() == 0 || str.charAt(str.length() - 1) == '\n') { + return; + } + + str.append('\n'); + } + + private void addBlankIfNoneBefore() { + if (str.length() == 0 || Character.isWhitespace(str.charAt(str.length() - 1))) { + return; + } + + str.append(' '); + } + + private void addBlankOrNewLineIfNoneBefore() { + if (keepMultiline) { + addNewLineIfNoneBefore(); + } else { + addBlankIfNoneBefore(); + } + } + + @Override + public void head(Node node, int depth) { + String name = node.nodeName(); + if (node instanceof TextNode) { + String internalText = ((TextNode) node).text(); + // Internal text does not contains any formatting (as defined by HTML except inside "pre" tag) so multiple + // blanks are equals to 1 and newline and stuff is the same, so replace them all by a single space + // pre tags does not seem to be used by Capella editors so do not handle it + String pureText = MULTI_BLANKS_PATTERN.matcher(internalText).replaceAll(" "); + + // Add a new line before each occurrence of "- " + pureText = pureText.replaceAll("- ", "\n- "); + + pureText = pureText.trim(); + str.append(pureText); + } else if (name.equals("li")) { + // Basic handling of list items (no difference between numbered and dotted nor depth) + if (keepMultiline) { + addNewLineIfNoneBefore(); + str.append(" * "); + } else { + addBlankIfNoneBefore(); + str.append("* "); + } + } else if (Arrays.asList("p", "h1", "h2", "h3", "h4", "h5", "tr").contains(name)) { + if (str.length() > 0 && str.charAt(str.length() - 1) == '-') { + // Prevents adding a new line right after a dash + str.append(' '); + } else { + addBlankOrNewLineIfNoneBefore(); + } + } + } + + @Override + public void tail(Node node, int depth) { + String name = node.nodeName(); + if (name.equals("br")) { + str.append(keepMultiline ? '\n' : ' '); + } else if (Arrays.asList("li", "p", "h1", "h2", "h3", "h4", "h5").contains(name)) { + addBlankOrNewLineIfNoneBefore(); + } + } + + @Override + public String toString() { + return str.toString(); + } + } +} diff --git a/releng/org.polarsys.capella.vp.requirements.target/tp/capella.target-definition.targetplatform b/releng/org.polarsys.capella.vp.requirements.target/tp/capella.target-definition.targetplatform index 11304686..573a2906 100644 --- a/releng/org.polarsys.capella.vp.requirements.target/tp/capella.target-definition.targetplatform +++ b/releng/org.polarsys.capella.vp.requirements.target/tp/capella.target-definition.targetplatform @@ -46,4 +46,8 @@ location xhtml "https://download.eclipse.org/capella/addons/xhtmldocgen/updates/ location orbit "https://download.eclipse.org/tools/orbit/downloads/2023-03" { org.apache.commons.io lazy +} + +location eclipse "https://download.eclipse.org/releases/2023-03" { + org.jsoup lazy } \ No newline at end of file