2018-06-12 20:50:45 +00:00
|
|
|
package net.sourceforge.plantuml.preproc;
|
|
|
|
|
2021-09-13 05:18:15 +00:00
|
|
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
|
|
|
|
2018-06-12 20:50:45 +00:00
|
|
|
import java.io.BufferedReader;
|
|
|
|
import java.io.ByteArrayInputStream;
|
|
|
|
import java.io.DataInputStream;
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.io.InputStream;
|
|
|
|
import java.io.InputStreamReader;
|
|
|
|
import java.io.PrintWriter;
|
|
|
|
import java.lang.ref.SoftReference;
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Collection;
|
|
|
|
import java.util.Collections;
|
|
|
|
import java.util.HashMap;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.Map;
|
|
|
|
import java.util.Set;
|
|
|
|
import java.util.TreeSet;
|
|
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
|
|
import java.util.regex.Matcher;
|
|
|
|
import java.util.regex.Pattern;
|
|
|
|
|
|
|
|
import net.sourceforge.plantuml.Log;
|
|
|
|
import net.sourceforge.plantuml.brotli.BrotliInputStream;
|
2022-08-17 17:34:24 +00:00
|
|
|
import net.sourceforge.plantuml.log.Logme;
|
2020-05-30 15:20:23 +00:00
|
|
|
import net.sourceforge.plantuml.security.SFile;
|
2018-06-12 20:50:45 +00:00
|
|
|
|
|
|
|
public class Stdlib {
|
|
|
|
|
|
|
|
private static final Map<String, Stdlib> all = new ConcurrentHashMap<String, Stdlib>();
|
|
|
|
private static final String SEPARATOR = "\uF8FF";
|
|
|
|
private static final Pattern sizePattern = Pattern.compile("\\[(\\d+)x(\\d+)/16\\]");
|
|
|
|
|
|
|
|
private final Map<String, SoftReference<String>> cache = new ConcurrentHashMap<String, SoftReference<String>>();
|
|
|
|
private final String name;
|
|
|
|
private final Map<String, String> info = new HashMap<String, String>();
|
|
|
|
|
|
|
|
public static InputStream getResourceAsStream(String fullname) {
|
|
|
|
fullname = fullname.toLowerCase().replace(".puml", "");
|
|
|
|
final int last = fullname.indexOf('/');
|
2022-08-17 17:34:24 +00:00
|
|
|
if (last == -1)
|
2018-06-12 20:50:45 +00:00
|
|
|
return null;
|
2022-08-17 17:34:24 +00:00
|
|
|
|
2018-06-12 20:50:45 +00:00
|
|
|
try {
|
|
|
|
final Stdlib folder = retrieve(fullname.substring(0, last));
|
2022-08-17 17:34:24 +00:00
|
|
|
if (folder == null || folder.info.size() == 0)
|
2018-06-12 20:50:45 +00:00
|
|
|
return null;
|
2022-08-17 17:34:24 +00:00
|
|
|
|
2018-06-12 20:50:45 +00:00
|
|
|
final String data = folder.loadRessource(fullname.substring(last + 1));
|
2022-08-17 17:34:24 +00:00
|
|
|
if (data == null)
|
2018-06-12 20:50:45 +00:00
|
|
|
return null;
|
2022-08-17 17:34:24 +00:00
|
|
|
|
2021-09-13 05:18:15 +00:00
|
|
|
return new ByteArrayInputStream(data.getBytes(UTF_8));
|
2018-06-12 20:50:45 +00:00
|
|
|
} catch (IOException e) {
|
2022-08-17 17:34:24 +00:00
|
|
|
Logme.error(e);
|
2018-06-12 20:50:45 +00:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-18 21:24:31 +00:00
|
|
|
public static Stdlib retrieve(final String name) throws IOException {
|
2018-06-12 20:50:45 +00:00
|
|
|
Stdlib result = all.get(name);
|
|
|
|
if (result == null) {
|
|
|
|
final DataInputStream dataStream = getDataStream(name);
|
2022-08-17 17:34:24 +00:00
|
|
|
if (dataStream == null)
|
2018-06-12 20:50:45 +00:00
|
|
|
return null;
|
2022-08-17 17:34:24 +00:00
|
|
|
|
2018-06-12 20:50:45 +00:00
|
|
|
final String info = dataStream.readUTF();
|
|
|
|
dataStream.close();
|
|
|
|
result = new Stdlib(name, info);
|
|
|
|
all.put(name, result);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
private String loadRessource(String file) throws IOException {
|
|
|
|
final SoftReference<String> cached = cache.get(file.toLowerCase());
|
|
|
|
if (cached != null) {
|
|
|
|
final String cachedResult = cached.get();
|
|
|
|
if (cachedResult != null) {
|
2018-07-27 21:56:46 +00:00
|
|
|
// Log.info("Using cache for " + file);
|
2018-06-12 20:50:45 +00:00
|
|
|
return cachedResult;
|
|
|
|
}
|
|
|
|
}
|
2018-07-27 21:56:46 +00:00
|
|
|
Log.info("No cache for " + file);
|
2018-06-12 20:50:45 +00:00
|
|
|
final DataInputStream dataStream = getDataStream();
|
2022-08-17 17:34:24 +00:00
|
|
|
if (dataStream == null)
|
2018-06-12 20:50:45 +00:00
|
|
|
return null;
|
2022-08-17 17:34:24 +00:00
|
|
|
|
2018-06-12 20:50:45 +00:00
|
|
|
dataStream.readUTF();
|
|
|
|
final InputStream spriteStream = getSpriteStream();
|
|
|
|
if (spriteStream == null) {
|
|
|
|
dataStream.close();
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
StringBuilder found = null;
|
|
|
|
while (true) {
|
|
|
|
final String filename = dataStream.readUTF();
|
|
|
|
if (filename.equals(SEPARATOR)) {
|
2018-07-27 21:56:46 +00:00
|
|
|
Log.info("Not found " + filename);
|
2018-06-12 20:50:45 +00:00
|
|
|
return null;
|
|
|
|
}
|
2022-08-17 17:34:24 +00:00
|
|
|
if (filename.equalsIgnoreCase(file))
|
2018-06-12 20:50:45 +00:00
|
|
|
found = new StringBuilder();
|
2022-08-17 17:34:24 +00:00
|
|
|
|
2018-06-12 20:50:45 +00:00
|
|
|
while (true) {
|
|
|
|
final String s = dataStream.readUTF();
|
|
|
|
if (s.equals(SEPARATOR)) {
|
|
|
|
if (found != null) {
|
|
|
|
final String result = found.toString();
|
2021-05-14 08:42:57 +00:00
|
|
|
cache.put(file.toLowerCase(), new SoftReference<>(result));
|
2018-06-12 20:50:45 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (found != null) {
|
|
|
|
found.append(s);
|
|
|
|
found.append("\n");
|
|
|
|
}
|
|
|
|
if (isSpriteLine(s)) {
|
|
|
|
final Matcher m = sizePattern.matcher(s);
|
|
|
|
final boolean ok = m.find();
|
2022-08-17 17:34:24 +00:00
|
|
|
if (ok == false)
|
2018-06-12 20:50:45 +00:00
|
|
|
throw new IOException(s);
|
2022-08-17 17:34:24 +00:00
|
|
|
|
2018-06-12 20:50:45 +00:00
|
|
|
final int width = Integer.parseInt(m.group(1));
|
|
|
|
final int height = Integer.parseInt(m.group(2));
|
2018-07-27 21:56:46 +00:00
|
|
|
if (found == null) {
|
|
|
|
skipSprite(width, height, spriteStream);
|
|
|
|
} else {
|
|
|
|
final String sprite = readSprite(width, height, spriteStream);
|
2018-06-12 20:50:45 +00:00
|
|
|
found.append(sprite);
|
|
|
|
found.append("}\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
dataStream.close();
|
|
|
|
spriteStream.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
private Stdlib(String name, String info) throws IOException {
|
|
|
|
this.name = name;
|
|
|
|
fillMap(info);
|
|
|
|
}
|
|
|
|
|
2018-07-27 21:56:46 +00:00
|
|
|
private void skipSprite(int width, int height, InputStream inputStream) throws IOException {
|
|
|
|
final int nbLines = (height + 1) / 2;
|
|
|
|
inputStream.skip(nbLines * width);
|
|
|
|
}
|
|
|
|
|
2018-06-12 20:50:45 +00:00
|
|
|
private String readSprite(int width, int height, InputStream inputStream) throws IOException {
|
|
|
|
final int nbLines = (height + 1) / 2;
|
2018-07-27 21:56:46 +00:00
|
|
|
final StringBuilder result = new StringBuilder();
|
2018-06-12 20:50:45 +00:00
|
|
|
int line = 0;
|
|
|
|
for (int j = 0; j < nbLines; j++) {
|
|
|
|
final StringBuilder sb1 = new StringBuilder();
|
|
|
|
final StringBuilder sb2 = new StringBuilder();
|
|
|
|
for (int i = 0; i < width; i++) {
|
|
|
|
final int b = inputStream.read();
|
|
|
|
final int b1 = (b & 0xF0) >> 4;
|
|
|
|
final int b2 = (b & 0x0F);
|
|
|
|
sb1.append(toHexString(b1));
|
|
|
|
sb2.append(toHexString(b2));
|
|
|
|
}
|
|
|
|
result.append(sb1.toString());
|
|
|
|
result.append("\n");
|
|
|
|
line++;
|
|
|
|
if (line < height) {
|
|
|
|
result.append(sb2.toString());
|
|
|
|
result.append("\n");
|
|
|
|
line++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result.toString();
|
|
|
|
}
|
|
|
|
|
|
|
|
private String toHexString(final int b) {
|
|
|
|
return Integer.toHexString(b).toUpperCase();
|
|
|
|
}
|
|
|
|
|
|
|
|
private boolean isSpriteLine(String s) {
|
|
|
|
return s.trim().startsWith("sprite") && s.trim().endsWith("{");
|
|
|
|
}
|
|
|
|
|
|
|
|
private void fillMap(String infoString) {
|
2022-08-17 17:34:24 +00:00
|
|
|
for (String s : infoString.split("\n"))
|
2018-06-12 20:50:45 +00:00
|
|
|
if (s.contains("=")) {
|
|
|
|
final String data[] = s.split("=");
|
|
|
|
this.info.put(data[0], data[1]);
|
|
|
|
}
|
2022-08-17 17:34:24 +00:00
|
|
|
|
2018-06-12 20:50:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private static DataInputStream getDataStream(String name) throws IOException {
|
|
|
|
final InputStream raw = getInternalInputStream(name, "-abx.repx");
|
2022-08-17 17:34:24 +00:00
|
|
|
if (raw == null)
|
2018-06-12 20:50:45 +00:00
|
|
|
return null;
|
2022-08-17 17:34:24 +00:00
|
|
|
|
2018-06-12 20:50:45 +00:00
|
|
|
return new DataInputStream(new BrotliInputStream(raw));
|
|
|
|
}
|
|
|
|
|
|
|
|
private DataInputStream getDataStream() throws IOException {
|
|
|
|
return getDataStream(name);
|
|
|
|
}
|
|
|
|
|
|
|
|
private InputStream getSpriteStream() throws IOException {
|
|
|
|
final InputStream raw = getInternalInputStream(name, "-dex.repx");
|
2022-08-17 17:34:24 +00:00
|
|
|
if (raw == null)
|
2018-06-12 20:50:45 +00:00
|
|
|
return null;
|
2022-08-17 17:34:24 +00:00
|
|
|
|
2018-06-12 20:50:45 +00:00
|
|
|
return new BrotliInputStream(raw);
|
|
|
|
}
|
|
|
|
|
|
|
|
private static InputStream getInternalInputStream(String fullname, String extension) {
|
|
|
|
final String res = "/stdlib/" + fullname + extension;
|
|
|
|
return Stdlib.class.getResourceAsStream(res);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void extractStdLib() throws IOException {
|
|
|
|
for (String name : getAll()) {
|
|
|
|
final Stdlib folder = Stdlib.retrieve(name);
|
2020-02-18 21:24:31 +00:00
|
|
|
folder.extractMeFull();
|
2018-06-12 20:50:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static Collection<String> getAll() throws IOException {
|
2021-05-14 08:42:57 +00:00
|
|
|
final Set<String> result = new TreeSet<>();
|
2018-06-12 20:50:45 +00:00
|
|
|
final InputStream home = getInternalInputStream("home", ".repx");
|
|
|
|
final BufferedReader br = new BufferedReader(new InputStreamReader(home));
|
|
|
|
String name;
|
2022-08-17 17:34:24 +00:00
|
|
|
while ((name = br.readLine()) != null)
|
2018-06-12 20:50:45 +00:00
|
|
|
result.add(name);
|
2022-08-17 17:34:24 +00:00
|
|
|
|
2018-06-12 20:50:45 +00:00
|
|
|
return Collections.unmodifiableCollection(result);
|
|
|
|
}
|
|
|
|
|
2020-02-18 21:24:31 +00:00
|
|
|
private void extractMeFull() throws IOException {
|
2018-06-12 20:50:45 +00:00
|
|
|
final DataInputStream dataStream = getDataStream();
|
2022-08-17 17:34:24 +00:00
|
|
|
if (dataStream == null)
|
2018-06-12 20:50:45 +00:00
|
|
|
return;
|
2022-08-17 17:34:24 +00:00
|
|
|
|
2018-06-12 20:50:45 +00:00
|
|
|
dataStream.readUTF();
|
|
|
|
final InputStream spriteStream = getSpriteStream();
|
|
|
|
try {
|
|
|
|
while (true) {
|
|
|
|
final String filename = dataStream.readUTF();
|
2022-08-17 17:34:24 +00:00
|
|
|
if (filename.equals(SEPARATOR))
|
2018-06-12 20:50:45 +00:00
|
|
|
return;
|
2022-08-17 17:34:24 +00:00
|
|
|
|
2020-05-30 15:20:23 +00:00
|
|
|
final SFile f = new SFile("stdlib/" + name + "/" + filename + ".puml");
|
2018-06-12 20:50:45 +00:00
|
|
|
f.getParentFile().mkdirs();
|
2020-05-30 15:20:23 +00:00
|
|
|
final PrintWriter fos = f.createPrintWriter();
|
2018-06-12 20:50:45 +00:00
|
|
|
while (true) {
|
|
|
|
final String s = dataStream.readUTF();
|
2022-08-17 17:34:24 +00:00
|
|
|
if (s.equals(SEPARATOR))
|
2018-06-12 20:50:45 +00:00
|
|
|
break;
|
2022-08-17 17:34:24 +00:00
|
|
|
|
2018-06-12 20:50:45 +00:00
|
|
|
fos.println(s);
|
|
|
|
if (isSpriteLine(s)) {
|
|
|
|
final Matcher m = sizePattern.matcher(s);
|
|
|
|
final boolean ok = m.find();
|
2022-08-17 17:34:24 +00:00
|
|
|
if (ok == false)
|
2018-06-12 20:50:45 +00:00
|
|
|
throw new IOException(s);
|
2022-08-17 17:34:24 +00:00
|
|
|
|
2018-06-12 20:50:45 +00:00
|
|
|
final int width = Integer.parseInt(m.group(1));
|
|
|
|
final int height = Integer.parseInt(m.group(2));
|
|
|
|
final String sprite = readSprite(width, height, spriteStream);
|
|
|
|
fos.println(sprite);
|
|
|
|
fos.println("}");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fos.close();
|
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
dataStream.close();
|
|
|
|
spriteStream.close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-18 21:24:31 +00:00
|
|
|
public List<String> extractAllSprites() throws IOException {
|
2021-05-14 08:42:57 +00:00
|
|
|
final List<String> result = new ArrayList<>();
|
2020-02-18 21:24:31 +00:00
|
|
|
final DataInputStream dataStream = getDataStream();
|
2022-08-17 17:34:24 +00:00
|
|
|
if (dataStream == null)
|
2020-02-18 21:24:31 +00:00
|
|
|
return Collections.unmodifiableList(result);
|
2022-08-17 17:34:24 +00:00
|
|
|
|
2020-02-18 21:24:31 +00:00
|
|
|
dataStream.readUTF();
|
|
|
|
final InputStream spriteStream = getSpriteStream();
|
|
|
|
try {
|
|
|
|
while (true) {
|
|
|
|
final String filename = dataStream.readUTF();
|
2022-08-17 17:34:24 +00:00
|
|
|
if (filename.equals(SEPARATOR))
|
2020-02-18 21:24:31 +00:00
|
|
|
return Collections.unmodifiableList(result);
|
2022-08-17 17:34:24 +00:00
|
|
|
|
2020-02-18 21:24:31 +00:00
|
|
|
while (true) {
|
|
|
|
final String s = dataStream.readUTF();
|
2022-08-17 17:34:24 +00:00
|
|
|
if (s.equals(SEPARATOR))
|
2020-02-18 21:24:31 +00:00
|
|
|
break;
|
2022-08-17 17:34:24 +00:00
|
|
|
|
2020-02-18 21:24:31 +00:00
|
|
|
if (isSpriteLine(s)) {
|
|
|
|
final Matcher m = sizePattern.matcher(s);
|
|
|
|
final boolean ok = m.find();
|
2022-08-17 17:34:24 +00:00
|
|
|
if (ok == false)
|
2020-02-18 21:24:31 +00:00
|
|
|
throw new IOException(s);
|
2022-08-17 17:34:24 +00:00
|
|
|
|
2020-02-18 21:24:31 +00:00
|
|
|
final int width = Integer.parseInt(m.group(1));
|
|
|
|
final int height = Integer.parseInt(m.group(2));
|
|
|
|
final String sprite = readSprite(width, height, spriteStream);
|
2022-08-17 17:34:24 +00:00
|
|
|
if (s.contains("_LARGE") == false)
|
2020-02-18 21:24:31 +00:00
|
|
|
result.add(s + "\n" + sprite + "}");
|
2022-08-17 17:34:24 +00:00
|
|
|
|
2020-02-18 21:24:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
dataStream.close();
|
|
|
|
spriteStream.close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-12 20:50:45 +00:00
|
|
|
public static void addInfoVersion(List<String> strings, boolean details) {
|
|
|
|
try {
|
|
|
|
for (String name : getAll()) {
|
|
|
|
final Stdlib folder = Stdlib.retrieve(name);
|
|
|
|
if (details) {
|
|
|
|
strings.add("<b>" + name);
|
|
|
|
strings.add("Version " + folder.getVersion());
|
|
|
|
strings.add("Delivered by " + folder.getSource());
|
|
|
|
strings.add(" ");
|
|
|
|
} else {
|
|
|
|
strings.add("* " + name + " (Version " + folder.getVersion() + ")");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (IOException e) {
|
|
|
|
Log.error("Error " + e);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private String getVersion() {
|
|
|
|
return info.get("VERSION");
|
|
|
|
}
|
|
|
|
|
|
|
|
private String getSource() {
|
|
|
|
return info.get("SOURCE");
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void printStdLib() {
|
2021-05-14 08:42:57 +00:00
|
|
|
final List<String> print = new ArrayList<>();
|
2018-06-12 20:50:45 +00:00
|
|
|
addInfoVersion(print, true);
|
2022-08-17 17:34:24 +00:00
|
|
|
for (String s : print)
|
2018-06-12 20:50:45 +00:00
|
|
|
System.out.println(s.replace("<b>", ""));
|
2022-08-17 17:34:24 +00:00
|
|
|
|
2018-06-12 20:50:45 +00:00
|
|
|
}
|
|
|
|
}
|