From b32500bb61ae617bb312496d6d832e4be8190797 Mon Sep 17 00:00:00 2001 From: Arnaud Roques Date: Mon, 12 Jun 2023 22:49:47 +0200 Subject: [PATCH] fix: improve filelist support and nwdiag --- gradle.properties | 2 +- .../plantuml/file/AParentFolderRegular.java | 10 +- .../plantuml/filesdiagram/FilesEntry.java | 111 ++++++++++++++++++ .../plantuml/filesdiagram/FilesListing.java | 20 +--- .../plantuml/filesdiagram/FilesType.java | 40 +++++++ .../plantuml/klimt/sprite/SpriteColor.java | 30 ++--- .../plantuml/nwdiag/NwDiagram.java | 16 +++ .../plantuml/nwdiag/core/NServer.java | 2 + .../sourceforge/plantuml/security/SURL.java | 8 +- .../plantuml/tim/TFunctionImpl.java | 63 +++++----- .../plantuml/version/LicenseInfo.java | 55 +++++---- .../sourceforge/plantuml/version/Version.java | 2 +- 12 files changed, 259 insertions(+), 100 deletions(-) create mode 100644 src/net/sourceforge/plantuml/filesdiagram/FilesEntry.java create mode 100644 src/net/sourceforge/plantuml/filesdiagram/FilesType.java diff --git a/gradle.properties b/gradle.properties index 0d4293c8e..fe3b6bc48 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ # Warning, "version" should be the same in gradle.properties and Version.java # Any idea anyone how to magically synchronize those :-) ? -version = 1.2023.9beta3 +version = 1.2023.9beta4 org.gradle.workers.max = 3 \ No newline at end of file diff --git a/src/net/sourceforge/plantuml/file/AParentFolderRegular.java b/src/net/sourceforge/plantuml/file/AParentFolderRegular.java index 439c25342..0a7161c12 100644 --- a/src/net/sourceforge/plantuml/file/AParentFolderRegular.java +++ b/src/net/sourceforge/plantuml/file/AParentFolderRegular.java @@ -57,15 +57,15 @@ public class AParentFolderRegular implements AParentFolder { final SFile filecurrent; // Log.info("AParentFolderRegular::looking for " + nameOrPath); // Log.info("AParentFolderRegular::dir = " + dir); - if (dir == null) { + if (dir == null) filecurrent = new SFile(nameOrPath); - } else { + else filecurrent = dir.getAbsoluteFile().file(nameOrPath); - } + // Log.info("AParentFolderRegular::Filecurrent " + filecurrent); - if (filecurrent.exists()) { + if (filecurrent.exists()) return new AFileRegular(filecurrent.getCanonicalFile()); - } + return null; } diff --git a/src/net/sourceforge/plantuml/filesdiagram/FilesEntry.java b/src/net/sourceforge/plantuml/filesdiagram/FilesEntry.java new file mode 100644 index 000000000..744d49514 --- /dev/null +++ b/src/net/sourceforge/plantuml/filesdiagram/FilesEntry.java @@ -0,0 +1,111 @@ +/* ======================================================================== + * PlantUML : a free UML diagram generator + * ======================================================================== + * + * (C) Copyright 2009-2024, Arnaud Roques + * + * Project Info: https://plantuml.com + * + * If you like this project or if you find it useful, you can support us at: + * + * https://plantuml.com/patreon (only 1$ per month!) + * https://plantuml.com/paypal + * + * 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 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. + * + * + * Original Author: Arnaud Roques + * + */ +package net.sourceforge.plantuml.filesdiagram; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import net.sourceforge.plantuml.klimt.UTranslate; +import net.sourceforge.plantuml.klimt.creole.Display; +import net.sourceforge.plantuml.klimt.drawing.UGraphic; +import net.sourceforge.plantuml.klimt.font.FontConfiguration; +import net.sourceforge.plantuml.klimt.geom.HorizontalAlignment; +import net.sourceforge.plantuml.klimt.shape.TextBlock; +import net.sourceforge.plantuml.style.ISkinParam; + +public class FilesEntry implements Iterable { + + private final String name; + private FilesType type; + private List children = new ArrayList<>(); + + public FilesEntry(String name, FilesType type) { + this.name = name; + this.type = type; + } + + public FilesEntry addRawEntry(String raw) { + final int x = raw.indexOf('/'); + if (x == -1) { + final FilesEntry result = new FilesEntry(raw, FilesType.DATA); + children.add(result); + return result; + } + final FilesEntry folder = getOrCreateFolder(raw.substring(0, x)); + final String remain = raw.substring(x + 1); + if (remain.length() == 0) + return folder; + return folder.addRawEntry(remain); + } + + private FilesEntry getOrCreateFolder(String folderName) { + for (FilesEntry child : children) + if (child.type == FilesType.FOLDER && child.getName().equals(folderName)) + return child; + + final FilesEntry result = new FilesEntry(folderName, FilesType.FOLDER); + children.add(result); + return result; + } + + @Override + public Iterator iterator() { + return Collections.unmodifiableCollection(children).iterator(); + } + + public String getName() { + return name; + } + + public String getEmoticon() { + if (type == FilesType.FOLDER) + return "<:1f4c2:>"; + // return "<:1f4c1:>"; + return "<:1f4c4:>"; + } + + public UGraphic drawAndMove(UGraphic ug, FontConfiguration fontConfiguration, ISkinParam skinParam, double deltax) { + final Display display = Display.getWithNewlines(getEmoticon() + getName()); + TextBlock result = display.create(fontConfiguration, HorizontalAlignment.LEFT, skinParam); + result.drawU(ug.apply(UTranslate.dx(deltax))); + ug = ug.apply(UTranslate.dy(result.calculateDimension(ug.getStringBounder()).getHeight() + 2)); + for (FilesEntry child : children) + ug = child.drawAndMove(ug, fontConfiguration, skinParam, deltax + 21); + return ug; + } + +} diff --git a/src/net/sourceforge/plantuml/filesdiagram/FilesListing.java b/src/net/sourceforge/plantuml/filesdiagram/FilesListing.java index 99be97712..097e93e69 100644 --- a/src/net/sourceforge/plantuml/filesdiagram/FilesListing.java +++ b/src/net/sourceforge/plantuml/filesdiagram/FilesListing.java @@ -34,26 +34,19 @@ */ package net.sourceforge.plantuml.filesdiagram; -import java.util.ArrayList; -import java.util.List; - -import net.sourceforge.plantuml.klimt.UTranslate; -import net.sourceforge.plantuml.klimt.creole.Display; import net.sourceforge.plantuml.klimt.drawing.UGraphic; import net.sourceforge.plantuml.klimt.font.FontConfiguration; import net.sourceforge.plantuml.klimt.font.StringBounder; import net.sourceforge.plantuml.klimt.font.UFont; -import net.sourceforge.plantuml.klimt.geom.HorizontalAlignment; import net.sourceforge.plantuml.klimt.geom.XDimension2D; import net.sourceforge.plantuml.klimt.shape.AbstractTextBlock; -import net.sourceforge.plantuml.klimt.shape.TextBlock; import net.sourceforge.plantuml.style.ISkinParam; public class FilesListing extends AbstractTextBlock { private final ISkinParam skinParam; private final FontConfiguration fontConfiguration = FontConfiguration.blackBlueTrue(UFont.courier(14)); - private final List tmp = new ArrayList<>(); + private final FilesEntry root = new FilesEntry("", FilesType.FOLDER); public FilesListing(ISkinParam skinParam) { this.skinParam = skinParam; @@ -66,18 +59,13 @@ public class FilesListing extends AbstractTextBlock { @Override public void drawU(UGraphic ug) { - for (String s : tmp) { - final Display display = Display.getWithNewlines("<:1f4c4:>" + s); - TextBlock result = display.create(fontConfiguration, HorizontalAlignment.LEFT, skinParam); - result.drawU(ug); - ug = ug.apply(UTranslate.dy(result.calculateDimension(ug.getStringBounder()).getHeight())); - } - + for (FilesEntry ent : root) + ug = ent.drawAndMove(ug, fontConfiguration, skinParam, 0); } public void add(String line) { if (line.startsWith("/")) - tmp.add(line.substring(1)); + root.addRawEntry(line.substring(1)); } } diff --git a/src/net/sourceforge/plantuml/filesdiagram/FilesType.java b/src/net/sourceforge/plantuml/filesdiagram/FilesType.java new file mode 100644 index 000000000..4c6d98615 --- /dev/null +++ b/src/net/sourceforge/plantuml/filesdiagram/FilesType.java @@ -0,0 +1,40 @@ +/* ======================================================================== + * PlantUML : a free UML diagram generator + * ======================================================================== + * + * (C) Copyright 2009-2024, Arnaud Roques + * + * Project Info: https://plantuml.com + * + * If you like this project or if you find it useful, you can support us at: + * + * https://plantuml.com/patreon (only 1$ per month!) + * https://plantuml.com/paypal + * + * 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 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. + * + * + * Original Author: Arnaud Roques + * + */ +package net.sourceforge.plantuml.filesdiagram; + +public enum FilesType { + FOLDER, DATA; + +} diff --git a/src/net/sourceforge/plantuml/klimt/sprite/SpriteColor.java b/src/net/sourceforge/plantuml/klimt/sprite/SpriteColor.java index fbd3d4eb3..eba27c816 100644 --- a/src/net/sourceforge/plantuml/klimt/sprite/SpriteColor.java +++ b/src/net/sourceforge/plantuml/klimt/sprite/SpriteColor.java @@ -66,26 +66,26 @@ public class SpriteColor implements Sprite { } public void setGray(int x, int y, int level) { - if (x < 0 || x >= width) { + if (x < 0 || x >= width) return; - } - if (y < 0 || y >= height) { + + if (y < 0 || y >= height) return; - } - if (level < 0 || level >= 16) { + + if (level < 0 || level >= 16) throw new IllegalArgumentException(); - } + gray[y][x] = level; color[y][x] = -1; } public void setColor(int x, int y, int col) { - if (x < 0 || x >= width) { + if (x < 0 || x >= width) return; - } - if (y < 0 || y >= height) { + + if (y < 0 || y >= height) return; - } + gray[y][x] = -1; color[y][x] = col; } @@ -99,14 +99,14 @@ public class SpriteColor implements Sprite { } public UImage toUImage(ColorMapper colorMapper, HColor backcolor, HColor forecolor) { - final BufferedImage im = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + final BufferedImage im = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); - if (backcolor == null) { + if (backcolor == null) backcolor = HColors.WHITE; - } - if (forecolor == null) { + + if (forecolor == null) forecolor = HColors.BLACK; - } + final HColorGradient gradient = HColors.gradient(backcolor, forecolor, '\0'); for (int col = 0; col < width; col++) { for (int line = 0; line < height; line++) { diff --git a/src/net/sourceforge/plantuml/nwdiag/NwDiagram.java b/src/net/sourceforge/plantuml/nwdiag/NwDiagram.java index 2df339cd8..6bc30d64e 100644 --- a/src/net/sourceforge/plantuml/nwdiag/NwDiagram.java +++ b/src/net/sourceforge/plantuml/nwdiag/NwDiagram.java @@ -150,11 +150,27 @@ public class NwDiagram extends UmlDiagram { for (NStackable element : stack) if (element instanceof Network) return CommandExecutionResult.error("Cannot nest network"); + + if (networks.size() == 0 && groups.size() == 0) + eventuallyConnectAllStandaloneServersToHiddenNetwork(); + final Network network = createNetwork(name); stack.add(0, network); return CommandExecutionResult.ok(); } + private void eventuallyConnectAllStandaloneServersToHiddenNetwork() { + Network first = null; + for (NServer server : servers.values()) + if (server.isAlone()) { + if (first == null) { + first = createNetwork(""); + first.goInvisible(); + } + server.connectMeIfAlone(first); + } + } + public CommandExecutionResult closeSomething() { if (initDone == false) return errorNoInit(); diff --git a/src/net/sourceforge/plantuml/nwdiag/core/NServer.java b/src/net/sourceforge/plantuml/nwdiag/core/NServer.java index 9bba897a5..13265a2b7 100644 --- a/src/net/sourceforge/plantuml/nwdiag/core/NServer.java +++ b/src/net/sourceforge/plantuml/nwdiag/core/NServer.java @@ -112,6 +112,8 @@ public class NServer { } public void learnThisAddress(String address) { + if (address == null) + address = ""; for (Entry ent : connections.entrySet()) { if (ent.getValue().length() == 0) { connections.put(ent.getKey(), address); diff --git a/src/net/sourceforge/plantuml/security/SURL.java b/src/net/sourceforge/plantuml/security/SURL.java index 5bcff360d..019915851 100644 --- a/src/net/sourceforge/plantuml/security/SURL.java +++ b/src/net/sourceforge/plantuml/security/SURL.java @@ -250,7 +250,7 @@ public class SURL { /** * Regex to remove the UserInfo part from a URL. */ - private static final Pattern PATTERN_USERINFO = Pattern.compile("(^https?://)([-_0-9a-zA-Z]+@)([^@]*)"); + private static final Pattern PATTERN_USERINFO = Pattern.compile("(^https?://)([-_0-9a-zA-Z]+@)([^@]*)$"); private static final ExecutorService EXE = Executors.newCachedThreadPool(new ThreadFactory() { public Thread newThread(Runnable r) { @@ -292,6 +292,9 @@ public class SURL { } private boolean forbiddenURL(String full) { + // Thanks to Agasthya Kasturi + if (full.contains("@")) + return true; if (full.startsWith("https://") == false && full.startsWith("http://") == false) return true; if (full.matches("^https?://[-#.0-9:\\[\\]+]+/.*")) @@ -305,6 +308,9 @@ public class SURL { private boolean isInUrlAllowList() { final String full = cleanPath(internal.toString()); + // Thanks to Agasthya Kasturi + if (full.contains("@")) + return false; for (String allow : getUrlAllowList()) if (full.startsWith(cleanPath(allow))) return true; diff --git a/src/net/sourceforge/plantuml/tim/TFunctionImpl.java b/src/net/sourceforge/plantuml/tim/TFunctionImpl.java index 86417856e..dbdf7eb1a 100644 --- a/src/net/sourceforge/plantuml/tim/TFunctionImpl.java +++ b/src/net/sourceforge/plantuml/tim/TFunctionImpl.java @@ -60,9 +60,9 @@ public class TFunctionImpl implements TFunction { public TFunctionImpl(String functionName, List args, boolean unquoted, TFunctionType functionType) { final Set names = new HashSet<>(); - for (TFunctionArgument tmp : args) { + for (TFunctionArgument tmp : args) names.add(tmp.getName()); - } + this.signature = new TFunctionSignature(functionName, args.size(), names); this.args = args; this.unquoted = unquoted; @@ -70,27 +70,26 @@ public class TFunctionImpl implements TFunction { } public boolean canCover(int nbArg, Set namedArguments) { - for (String n : namedArguments) { - if (signature.getNamedArguments().contains(n) == false) { + for (String n : namedArguments) + if (signature.getNamedArguments().contains(n) == false) return false; - } - } - if (nbArg > args.size()) { + + if (nbArg > args.size()) return false; - } + assert nbArg <= args.size(); int neededArgument = 0; for (TFunctionArgument arg : args) { - if (namedArguments.contains(arg.getName())) { + if (namedArguments.contains(arg.getName())) continue; - } - if (arg.getOptionalDefaultValue() == null) { + + if (arg.getOptionalDefaultValue() == null) neededArgument++; - } + } - if (nbArg < neededArgument) { + if (nbArg < neededArgument) return false; - } + assert nbArg >= neededArgument; return true; } @@ -108,9 +107,9 @@ public class TFunctionImpl implements TFunction { } else { value = arg.getOptionalDefaultValue(); } - if (value == null) { + if (value == null) throw new IllegalStateException(); - } + result.put(arg.getName(), value); } return memory.forkFromGlobal(result); @@ -125,11 +124,9 @@ public class TFunctionImpl implements TFunction { body.add(s); if (s.getType() == TLineType.RETURN) { this.containsReturn = true; - if (functionType == TFunctionType.PROCEDURE) { + if (functionType == TFunctionType.PROCEDURE) throw EaterExceptionLocated .located("A procedure cannot have !return directive. Declare it as a function instead ?", s); - // this.functionType = TFunctionType.RETURN; - } } } @@ -147,39 +144,39 @@ public class TFunctionImpl implements TFunction { public void executeProcedureInternal(TContext context, TMemory memory, List args, Map named) throws EaterException, EaterExceptionLocated { - if (functionType != TFunctionType.PROCEDURE && functionType != TFunctionType.LEGACY_DEFINELONG) { + if (functionType != TFunctionType.PROCEDURE && functionType != TFunctionType.LEGACY_DEFINELONG) throw new IllegalStateException(); - } + final TMemory copy = getNewMemory(memory, args, named); context.executeLines(copy, body, TFunctionType.PROCEDURE, false); } public TValue executeReturnFunction(TContext context, TMemory memory, LineLocation location, List args, Map named) throws EaterException, EaterExceptionLocated { - if (functionType == TFunctionType.LEGACY_DEFINE) { + if (functionType == TFunctionType.LEGACY_DEFINE) return executeReturnLegacyDefine(location, context, memory, args); - } - if (functionType != TFunctionType.RETURN_FUNCTION) { + + if (functionType != TFunctionType.RETURN_FUNCTION) throw EaterException.unlocated("Illegal call here. Is there a return directive in your function?"); - } + final TMemory copy = getNewMemory(memory, args, named); final TValue result = context.executeLines(copy, body, TFunctionType.RETURN_FUNCTION, true); - if (result == null) { + if (result == null) throw EaterException.unlocated("No return directive found in your function"); - } + return result; } private TValue executeReturnLegacyDefine(LineLocation location, TContext context, TMemory memory, List args) throws EaterException, EaterExceptionLocated { - if (legacyDefinition == null) { + if (legacyDefinition == null) throw new IllegalStateException(); - } + final TMemory copy = getNewMemory(memory, args, Collections.emptyMap()); final String tmp = context.applyFunctionsAndVariables(copy, location, legacyDefinition); - if (tmp == null) { + if (tmp == null) return TValue.fromString(""); - } + return TValue.fromString(tmp); // eaterReturn.execute(context, copy); // // System.err.println("s3=" + eaterReturn.getValue2()); @@ -211,9 +208,9 @@ public class TFunctionImpl implements TFunction { } public void finalizeEnddefinelong() { - if (functionType != TFunctionType.LEGACY_DEFINELONG) { + if (functionType != TFunctionType.LEGACY_DEFINELONG) throw new UnsupportedOperationException(); - } + if (body.size() == 1) { this.functionType = TFunctionType.LEGACY_DEFINE; this.legacyDefinition = body.get(0).getString(); diff --git a/src/net/sourceforge/plantuml/version/LicenseInfo.java b/src/net/sourceforge/plantuml/version/LicenseInfo.java index a942f267a..bc8af9922 100644 --- a/src/net/sourceforge/plantuml/version/LicenseInfo.java +++ b/src/net/sourceforge/plantuml/version/LicenseInfo.java @@ -108,27 +108,27 @@ public class LicenseInfo { public static synchronized LicenseInfo retrieveNamedSlow() { cache = LicenseInfo.NONE; - if (OptionFlags.ALLOW_INCLUDE == false) { + if (OptionFlags.ALLOW_INCLUDE == false) return cache; - } + final String key = prefs.get("license", ""); if (key.length() > 0) { cache = setIfValid(retrieveNamed(key), cache); - if (cache.isValid()) { + if (cache.isValid()) return cache; - } + } for (SFile f : fileCandidates()) { try { if (f.exists() && f.canRead()) { final LicenseInfo result = retrieve(f); - if (result == null) { + if (result == null) return null; - } + cache = setIfValid(result, cache); - if (cache.isValid()) { + if (cache.isValid()) return cache; - } + } } catch (IOException e) { Log.info("Error " + e); @@ -157,13 +157,13 @@ public class LicenseInfo { } try { final byte[] s1 = PLSSignature.retrieveDistributorImageSignature(); - if (SignatureUtils.toHexString(s1).equals(SignatureUtils.toHexString(licenseInfo.sha)) == false) { + if (SignatureUtils.toHexString(s1).equals(SignatureUtils.toHexString(licenseInfo.sha)) == false) return null; - } + final InputStream dis = PSystemVersion.class.getResourceAsStream("/distributor.png"); - if (dis == null) { + if (dis == null) return null; - } + try { final BufferedImage result = SImageIO.read(dis); return result; @@ -178,9 +178,9 @@ public class LicenseInfo { public static LicenseInfo retrieveDistributor() { final InputStream dis = PSystemVersion.class.getResourceAsStream("/distributor.txt"); - if (dis == null) { + if (dis == null) return null; - } + try { final BufferedReader br = new BufferedReader(new InputStreamReader(dis)); final String licenseString = br.readLine(); @@ -188,11 +188,10 @@ public class LicenseInfo { final LicenseInfo result = PLSSignature.retrieveDistributor(licenseString); final Throwable creationPoint = new Throwable(); creationPoint.fillInStackTrace(); - for (StackTraceElement ste : creationPoint.getStackTrace()) { - if (ste.toString().contains(result.context)) { + for (StackTraceElement ste : creationPoint.getStackTrace()) + if (ste.toString().contains(result.context)) return result; - } - } + return null; } catch (Exception e) { Logme.error(e); @@ -208,34 +207,34 @@ public class LicenseInfo { if (s == null) continue; SFile dir = new SFile(s); - if (dir.isFile()) { + if (dir.isFile()) dir = dir.getParentFile(); - } - if (dir != null && dir.isDirectory()) { + + if (dir != null && dir.isDirectory()) result.add(dir.file("license.txt")); - } + } return result; } private static LicenseInfo setIfValid(LicenseInfo value, LicenseInfo def) { - if (value.isValid() || def.isNone()) { + if (value.isValid() || def.isNone()) return value; - } + return def; } private static LicenseInfo retrieve(SFile f) throws IOException { final BufferedReader br = f.openBufferedReader(); - if (br == null) { + if (br == null) return null; - } + try { final String s = br.readLine(); final LicenseInfo result = retrieveNamed(s); - if (result != null) { + if (result != null) Log.info("Reading license from " + f.getAbsolutePath()); - } + return result; } finally { br.close(); diff --git a/src/net/sourceforge/plantuml/version/Version.java b/src/net/sourceforge/plantuml/version/Version.java index 6eec116d6..bb24e3520 100644 --- a/src/net/sourceforge/plantuml/version/Version.java +++ b/src/net/sourceforge/plantuml/version/Version.java @@ -46,7 +46,7 @@ public class Version { // Warning, "version" should be the same in gradle.properties and Version.java // Any idea anyone how to magically synchronize those :-) ? - private static final String version = "1.2023.9beta3"; + private static final String version = "1.2023.9beta4"; public static String versionString() { return version;