1
0
mirror of https://github.com/octoleo/plantuml.git synced 2024-11-21 20:45:10 +00:00

refactor: remove unused code

This commit is contained in:
Arnaud Roques 2023-10-22 11:54:17 +02:00
parent 8f5c3379ed
commit e92dcd400b
15 changed files with 18 additions and 2084 deletions

View File

@ -4,4 +4,6 @@ This document catalogs code that was previously part of PlantUML but has since b
It serves as a historical reference to ensure we remember and understand past decisions.
- [logo language](https://github.com/plantuml/plantuml/tree/v1.2023.12/src/net/sourceforge/plantuml/logo)
- [basic language](https://github.com/plantuml/plantuml/tree/v1.2023.12/src/net/sourceforge/plantuml/jasic)
- [mjpeg export](https://github.com/plantuml/plantuml/tree/v1.2023.12/src/net/sourceforge/plantuml/mjpeg)
- [animation](https://github.com/plantuml/plantuml/tree/v1.2023.12/src/net/sourceforge/plantuml/anim)

View File

@ -37,8 +37,6 @@ package net.atmp;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@ -46,22 +44,16 @@ import java.io.OutputStream;
import java.util.Random;
import java.util.Set;
import javax.swing.ImageIcon;
import com.plantuml.api.cheerpj.WasmLog;
import net.sourceforge.plantuml.AnimatedGifEncoder;
import net.sourceforge.plantuml.AnnotatedBuilder;
import net.sourceforge.plantuml.AnnotatedWorker;
import net.sourceforge.plantuml.EmptyImageBuilder;
import net.sourceforge.plantuml.FileFormat;
import net.sourceforge.plantuml.FileFormatOption;
import net.sourceforge.plantuml.FileUtils;
import net.sourceforge.plantuml.OptionFlags;
import net.sourceforge.plantuml.Scale;
import net.sourceforge.plantuml.TitledDiagram;
import net.sourceforge.plantuml.anim.AffineTransformation;
import net.sourceforge.plantuml.anim.Animation;
import net.sourceforge.plantuml.api.ImageDataComplex;
import net.sourceforge.plantuml.api.ImageDataSimple;
import net.sourceforge.plantuml.braille.UGraphicBraille;
@ -87,14 +79,10 @@ import net.sourceforge.plantuml.klimt.drawing.tikz.UGraphicTikz;
import net.sourceforge.plantuml.klimt.drawing.txt.UGraphicTxt;
import net.sourceforge.plantuml.klimt.drawing.visio.UGraphicVdx;
import net.sourceforge.plantuml.klimt.font.StringBounder;
import net.sourceforge.plantuml.klimt.geom.MinMax;
import net.sourceforge.plantuml.klimt.geom.XDimension2D;
import net.sourceforge.plantuml.klimt.shape.TextBlock;
import net.sourceforge.plantuml.klimt.shape.UDrawable;
import net.sourceforge.plantuml.klimt.shape.URectangle;
import net.sourceforge.plantuml.mjpeg.MJPEGGenerator;
import net.sourceforge.plantuml.security.SFile;
import net.sourceforge.plantuml.security.SImageIO;
import net.sourceforge.plantuml.skin.ColorParam;
import net.sourceforge.plantuml.skin.CornerParam;
import net.sourceforge.plantuml.skin.LineParam;
@ -113,9 +101,6 @@ import net.sourceforge.plantuml.url.Url;
public class ImageBuilder {
// ::comment when __CORE__
private Animation animation;
// ::done
private boolean annotations;
private HColor backcolor = getDefaultHBackColor();
@ -223,9 +208,6 @@ public class ImageBuilder {
public ImageBuilder styled(TitledDiagram diagram) {
skinParam = diagram.getSkinParam();
stringBounder = fileFormatOption.getDefaultStringBounder(skinParam);
// ::comment when __CORE__
animation = diagram.getAnimation();
// ::done
annotations = true;
backcolor = diagram.calculateBackColor();
margin = calculateMargin(diagram);
@ -246,21 +228,7 @@ public class ImageBuilder {
udrawable = annotatedWorker.addAdd((TextBlock) udrawable);
}
// ::comment when __CORE__
switch (fileFormatOption.getFileFormat()) {
case MJPEG:
return writeImageMjpeg(os);
case ANIMATED_GIF:
return writeImageAnimatedGif(os);
default:
return writeImageInternal(os, animation);
// ::done
// ::uncomment when __CORE__
// return writeImageInternal(os);
// ::done
// ::comment when __CORE__
}
// ::done
return writeImageInternal(os);
}
public byte[] writeByteArray() throws IOException {
@ -270,34 +238,19 @@ public class ImageBuilder {
}
}
// ::revert when __CORE__
private ImageData writeImageInternal(OutputStream os, Animation animationArg) throws IOException {
// private ImageData writeImageInternal(OutputStream os) throws IOException {
// ::done
private ImageData writeImageInternal(OutputStream os) throws IOException {
XDimension2D dim = getFinalDimension();
double dx = 0;
double dy = 0;
// ::comment when __CORE__
if (animationArg != null) {
final MinMax minmax = animationArg.getMinMax(dim);
animationArg.setDimension(dim);
dim = minmax.getDimension();
dx = -minmax.getMinX();
dy = -minmax.getMinY();
}
// ::done
final Scale scale = titledDiagram == null ? null : titledDiagram.getScale();
final double scaleFactor = (scale == null ? 1 : scale.getScale(dim.getWidth(), dim.getHeight())) * getDpi()
/ 96.0;
if (scaleFactor <= 0)
throw new IllegalStateException("Bad scaleFactor");
WasmLog.log("...image drawing...");
// ::revert when __CORE__
UGraphic ug = createUGraphic(dim, animationArg, dx, dy, scaleFactor,
UGraphic ug = createUGraphic(dim, dx, dy, scaleFactor,
titledDiagram == null ? new Pragma() : titledDiagram.getPragma());
// UGraphic ug = createUGraphic(dim, dx, dy, scaleFactor,
// titledDiagram == null ? new Pragma() : titledDiagram.getPragma());
// ::done
maybeDrawBorder(ug, dim);
if (randomPixel)
drawRandomPoint(ug);
@ -330,9 +283,9 @@ public class ImageBuilder {
if (stroke == null)
return;
final URectangle rectangle = URectangle.build(dim.getWidth() - stroke.getThickness(),
dim.getHeight() - stroke.getThickness())
.rounded(skinParam.getRoundCorner(CornerParam.diagramBorder, null));
final URectangle rectangle = URectangle
.build(dim.getWidth() - stroke.getThickness(), dim.getHeight() - stroke.getThickness())
.rounded(skinParam.getRoundCorner(CornerParam.diagramBorder, null));
ug.apply(color == null ? HColors.BLACK : color).apply(stroke).draw(rectangle);
}
@ -364,83 +317,13 @@ public class ImageBuilder {
return ug;
}
// ::comment when __CORE__
private ImageData writeImageMjpeg(OutputStream os) throws IOException {
final XDimension2D dim = getFinalDimension();
final SFile f = new SFile("c:/tmp.avi");
final int nbframe = 100;
final MJPEGGenerator m = new MJPEGGenerator(f, getAviImage(null).getWidth(null),
getAviImage(null).getHeight(null), 12.0, nbframe);
for (int i = 0; i < nbframe; i++) {
// AffineTransform at = AffineTransform.getRotateInstance(1.0);
AffineTransform at = AffineTransform.getTranslateInstance(dim.getWidth() / 2, dim.getHeight() / 2);
at.rotate(90.0 * Math.PI / 180.0 * i / 100);
at.translate(-dim.getWidth() / 2, -dim.getHeight() / 2);
// final AffineTransform at = AffineTransform.getTranslateInstance(i, 0);
// final ImageIcon ii = new ImageIcon(getAviImage(at));
// m.addImage(ii.getImage());
throw new UnsupportedOperationException();
}
m.finishAVI();
FileUtils.copyToStream(f, os);
return createImageData(dim);
}
private ImageData writeImageAnimatedGif(OutputStream os) throws IOException {
final XDimension2D dim = getFinalDimension();
final MinMax minmax = animation.getMinMax(dim);
final AnimatedGifEncoder e = new AnimatedGifEncoder();
// e.setQuality(1);
e.setRepeat(0);
e.start(os);
// e.setDelay(1000); // 1 frame per sec
// e.setDelay(100); // 10 frame per sec
e.setDelay(60); // 16 frame per sec
// e.setDelay(50); // 20 frame per sec
for (AffineTransformation at : animation.getAll()) {
final ImageIcon ii = new ImageIcon(getAviImage(at));
e.addFrame((BufferedImage) ii.getImage());
}
e.finish();
return createImageData(dim);
}
private Image getAviImage(AffineTransformation affineTransform) throws IOException {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
writeImageInternal(baos, Animation.singleton(affineTransform));
baos.close();
return SImageIO.read(baos.toByteArray());
}
// ::done
// ::revert when __CORE__
private UGraphic createUGraphic(final XDimension2D dim, Animation animationArg, double dx, double dy,
double scaleFactor, Pragma pragma) {
// private UGraphic createUGraphic(final XDimension2D dim, double dx, double dy,
// double scaleFactor, Pragma pragma) {
// ::done
private UGraphic createUGraphic(final XDimension2D dim, double dx, double dy, double scaleFactor, Pragma pragma) {
final ColorMapper colorMapper = fileFormatOption.getColorMapper();
switch (fileFormatOption.getFileFormat()) {
case PNG:
case RAW:
// ::comment when __CORE__
return createUGraphicPNG(scaleFactor, dim, animationArg, dx, dy, fileFormatOption.getWatermark(),
return createUGraphicPNG(scaleFactor, dim, dx, dy, fileFormatOption.getWatermark(),
fileFormatOption.getFileFormat());
// ::done
// ::uncomment when __CORE__
// return createUGraphicPNG(scaleFactor, dim, dx, dy,
// fileFormatOption.getWatermark(), fileFormatOption.getFileFormat());
// ::done
case SVG:
return createUGraphicSVG(scaleFactor, dim, pragma);
// ::comment when __CORE__
@ -492,13 +375,8 @@ public class ImageBuilder {
}
// ::uncomment when __CORE__
// private UGraphic createUGraphicPNG(double scaleFactor, final XDimension2D
// dim, double dx, double dy, String watermark, FileFormat format) {
// ::done
// ::comment when __CORE__
private UGraphic createUGraphicPNG(double scaleFactor, final XDimension2D dim, Animation affineTransforms,
double dx, double dy, String watermark, FileFormat format) {
private UGraphic createUGraphicPNG(double scaleFactor, final XDimension2D dim, double dx, double dy,
String watermark, FileFormat format) {
// ::done
Color pngBackColor = new Color(0, 0, 0, 0);
@ -513,15 +391,9 @@ public class ImageBuilder {
(int) (dim.getHeight() * scaleFactor), pngBackColor, stringBounder);
final Graphics2D graphics2D = builder.getGraphics2D();
// ::comment when __CORE__
final UGraphicG2d ug = new UGraphicG2d(backcolor, fileFormatOption.getColorMapper(), stringBounder, graphics2D,
scaleFactor, dx, dy, format, affineTransforms == null ? null : affineTransforms.getFirst());
// ::done
// ::uncomment when __CORE__
// final UGraphicG2d ug = new UGraphicG2d(backcolor,
// fileFormatOption.getColorMapper(), stringBounder, graphics2D,
// scaleFactor, dx, dy, format);
// ::done
scaleFactor, dx, dy, format);
ug.setBufferedImage(builder.getBufferedImage());
final BufferedImage im = ug.getBufferedImage();
if (this.backcolor instanceof HColorGradient)

View File

@ -76,8 +76,6 @@ public enum FileFormat {
SCXML("application/scxml+xml"), //
GRAPHML("application/graphml+xml"), //
PDF("application/pdf"), //
MJPEG("video/x-msvideo"), //
ANIMATED_GIF("image/gif"), //
HTML("text/html"), //
HTML5("text/html"), //
VDX("application/vnd.visio.xml"), //
@ -112,15 +110,9 @@ public enum FileFormat {
if (name().startsWith("XMI"))
return ".xmi";
if (this == MJPEG)
return ".avi";
if (this == LATEX || this == LATEX_NO_PREAMBLE)
return ".tex";
if (this == ANIMATED_GIF)
return ".gif";
if (this == BRAILLE_PNG)
return ".braille.png";

View File

@ -42,8 +42,6 @@ import java.util.Map;
import net.atmp.ImageBuilder;
import net.sourceforge.plantuml.abel.DisplayPositioned;
import net.sourceforge.plantuml.abel.DisplayPositionned;
import net.sourceforge.plantuml.anim.Animation;
import net.sourceforge.plantuml.anim.AnimationDecoder;
import net.sourceforge.plantuml.api.ApiStable;
import net.sourceforge.plantuml.command.CommandExecutionResult;
import net.sourceforge.plantuml.core.Diagram;
@ -88,10 +86,6 @@ public abstract class TitledDiagram extends AbstractPSystem implements Diagram,
private final SkinParam skinParam;
// ::comment when __CORE__
private Animation animation;
// ::done
private final Pragma pragma = new Pragma();
public Pragma getPragma() {
@ -218,21 +212,6 @@ public abstract class TitledDiagram extends AbstractPSystem implements Diagram,
return ClockwiseTopRightBottomLeft.same(10);
}
// ::comment when __CORE__
final public void setAnimation(Iterable<CharSequence> animationData) {
// try {
final AnimationDecoder animationDecoder = new AnimationDecoder(animationData);
this.animation = Animation.create(animationDecoder.decode());
// } catch (ScriptException e) {
// Logme.error(e);
// }
}
final public Animation getAnimation() {
return animation;
}
// ::done
@Override
public ImageBuilder createImageBuilder(FileFormatOption fileFormatOption) throws IOException {
return super.createImageBuilder(fileFormatOption).styled(this);

View File

@ -38,10 +38,8 @@ package net.sourceforge.plantuml;
import static net.atmp.ImageBuilder.plainImageBuilder;
import java.awt.Color;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
@ -74,10 +72,8 @@ import net.sourceforge.plantuml.klimt.shape.TextBlock;
import net.sourceforge.plantuml.klimt.shape.UDrawable;
import net.sourceforge.plantuml.klimt.shape.UImage;
import net.sourceforge.plantuml.log.Logme;
import net.sourceforge.plantuml.mjpeg.MJPEGGenerator;
import net.sourceforge.plantuml.pdf.PdfConverter;
import net.sourceforge.plantuml.security.SFile;
import net.sourceforge.plantuml.security.SImageIO;
import net.sourceforge.plantuml.security.SecurityUtils;
import net.sourceforge.plantuml.skin.UmlDiagramType;
import net.sourceforge.plantuml.style.NoStyleAvailableException;
@ -258,27 +254,6 @@ public abstract class UmlDiagram extends TitledDiagram implements Diagram, Annot
}
// ::comment when __CORE__
private void exportDiagramInternalMjpeg(OutputStream os) throws IOException {
final SFile f = new SFile("c:/test.avi");
final int nb = 150;
final double framerate = 30;
final MJPEGGenerator m = new MJPEGGenerator(f, 640, 480, framerate, nb);
for (int i = 0; i < nb; i++) {
final AffineTransform at = new AffineTransform();
final double coef = (nb - 1 - i) * 1.0 / nb;
at.setToShear(coef, coef);
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
// exportDiagramTOxxBEREMOVED(baos, null, 0, new
// FileFormatOption(FileFormat.PNG, at));
baos.close();
final BufferedImage im = SImageIO.read(baos.toByteArray());
m.addImage(im);
}
m.finishAVI();
}
private ImageData exportDiagramInternalPdf(OutputStream os, int index) throws IOException {
final File svg = FileUtils.createTempFileLegacy("pdf", ".svf");
final File pdfFile = FileUtils.createTempFileLegacy("pdf", ".pdf");

View File

@ -1,157 +0,0 @@
/* ========================================================================
* 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.anim;
import java.awt.geom.AffineTransform;
import java.util.Objects;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import net.sourceforge.plantuml.StringUtils;
import net.sourceforge.plantuml.klimt.geom.MinMax;
import net.sourceforge.plantuml.klimt.geom.XDimension2D;
import net.sourceforge.plantuml.klimt.geom.XPoint2D;
public class AffineTransformation {
// ::remove folder when __HAXE__
// ::remove folder when __CORE__
static private final Pattern rotate = Pattern.compile("rotate\\s+(-?\\d+\\.?\\d*)");
static private final Pattern shear = Pattern.compile("shear\\s+(-?\\d+\\.?\\d*)\\s+(-?\\d+\\.?\\d*)");
static private final Pattern translate = Pattern.compile("translate\\s+(-?\\d+\\.?\\d*)\\s+(-?\\d+\\.?\\d*)");
static private final Pattern scale = Pattern.compile("scale\\s+(-?\\d+\\.?\\d*)\\s+(-?\\d+\\.?\\d*)");
static private final Pattern color = Pattern.compile("color\\s+.*");
private final AffineTransform affineTransform;
private XDimension2D dimension;
private AffineTransformation(AffineTransform affineTransform) {
this.affineTransform = Objects.requireNonNull(affineTransform);
}
private AffineTransformation compose(AffineTransformation other) {
final AffineTransform tmp = new AffineTransform(this.affineTransform);
tmp.concatenate(other.affineTransform);
return new AffineTransformation(tmp);
}
public static AffineTransformation from(AffineTransform affineTransform) {
return new AffineTransformation(affineTransform);
}
static AffineTransformation create(String value) {
final StringTokenizer st = new StringTokenizer(value, "|");
AffineTransformation result = null;
while (st.hasMoreTokens()) {
final String s = st.nextToken();
final AffineTransformation tmp = createSimple(s);
if (tmp != null) {
if (result == null) {
result = tmp;
} else {
result = result.compose(tmp);
}
}
}
return result;
}
private static AffineTransformation createSimple(String value) {
Matcher m = rotate.matcher(StringUtils.trin(value));
if (m.find()) {
final double angle = Double.parseDouble(m.group(1));
return new AffineTransformation(AffineTransform.getRotateInstance(angle * Math.PI / 180.0));
}
m = shear.matcher(value);
if (m.find()) {
final double shx = Double.parseDouble(m.group(1));
final double shy = Double.parseDouble(m.group(2));
return new AffineTransformation(AffineTransform.getShearInstance(shx, shy));
}
m = translate.matcher(value);
if (m.find()) {
final double tx = Double.parseDouble(m.group(1));
final double ty = Double.parseDouble(m.group(2));
return new AffineTransformation(AffineTransform.getTranslateInstance(tx, ty));
}
m = scale.matcher(value);
if (m.find()) {
final double scalex = Double.parseDouble(m.group(1));
final double scaley = Double.parseDouble(m.group(2));
return new AffineTransformation(AffineTransform.getScaleInstance(scalex, scaley));
}
m = color.matcher(value);
if (m.find()) {
return new AffineTransformation(new AffineTransform());
}
return null;
}
public final AffineTransform getAffineTransform() {
return getAffineTransform(dimension);
}
private AffineTransform getAffineTransform(XDimension2D dimension) {
if (dimension == null) {
throw new IllegalStateException();
}
final AffineTransform at = AffineTransform.getTranslateInstance(dimension.getWidth() / 2,
dimension.getHeight() / 2);
at.concatenate(affineTransform);
at.translate(-dimension.getWidth() / 2, -dimension.getHeight() / 2);
return at;
}
public void setDimension(XDimension2D dim) {
this.dimension = dim;
}
public MinMax getMinMax(XDimension2D rect) {
MinMax result = MinMax.getEmpty(false);
final AffineTransform tmp = getAffineTransform(rect);
result = result.addPoint(new XPoint2D(0, 0).transform(tmp));
result = result.addPoint(new XPoint2D(0, rect.getHeight()).transform(tmp));
result = result.addPoint(new XPoint2D(rect.getWidth(), 0).transform(tmp));
result = result.addPoint(new XPoint2D(rect.getWidth(), rect.getHeight()).transform(tmp));
return result;
}
}

View File

@ -1,99 +0,0 @@
/* ========================================================================
* 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.anim;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import net.sourceforge.plantuml.klimt.geom.MinMax;
import net.sourceforge.plantuml.klimt.geom.XDimension2D;
public class Animation {
private final List<AffineTransformation> all;
private Animation(List<AffineTransformation> all) {
if (all.size() == 0) {
throw new IllegalArgumentException();
}
this.all = all;
}
public static Animation singleton(AffineTransformation affineTransformation) {
if (affineTransformation == null) {
return null;
}
return new Animation(Collections.singletonList(affineTransformation));
}
public static Animation create(List<String> descriptions) {
final List<AffineTransformation> all = new ArrayList<>();
for (String s : descriptions) {
final AffineTransformation tmp = AffineTransformation.create(s);
if (tmp != null) {
all.add(tmp);
}
}
return new Animation(all);
}
public Collection<AffineTransformation> getAll() {
return Collections.unmodifiableCollection(all);
}
public void setDimension(XDimension2D dim) {
for (AffineTransformation affineTransform : all) {
affineTransform.setDimension(dim);
}
}
public AffineTransformation getFirst() {
return all.get(0);
}
public MinMax getMinMax(XDimension2D dim) {
MinMax result = MinMax.getEmpty(false);
for (AffineTransformation affineTransform : all) {
final MinMax m = affineTransform.getMinMax(dim);
result = result.addMinMax(m);
}
return result;
}
}

View File

@ -1,78 +0,0 @@
/* ========================================================================
* 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.anim;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
public class AnimationDecoder {
private final List<String> result = new ArrayList<>();
public AnimationDecoder(Iterable<CharSequence> data) {
for (final Iterator<CharSequence> it = data.iterator(); it.hasNext();) {
String line = it.next().toString();
if (line.matches("^\\s*\\[script\\]\\s*$")) {
final StringBuilder scriptText = new StringBuilder();
while (true) {
line = it.next().toString();
if (line.matches("^\\s*\\[/script\\]\\s*$")) {
final AnimationScript script = new AnimationScript();
final String out = script.eval(scriptText.toString());
for (final StringTokenizer st = new StringTokenizer(out, "\n"); st.hasMoreTokens();) {
result.add(st.nextToken());
}
break;
} else {
scriptText.append(line);
scriptText.append("\n");
}
}
} else {
result.add(line);
}
}
}
public List<String> decode() {
return Collections.unmodifiableList(result);
}
}

View File

@ -1,71 +0,0 @@
/* ========================================================================
* 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.anim;
public class AnimationScript {
// private final ScriptEngine engine;
public AnimationScript() {
// final ScriptEngineManager manager = new ScriptEngineManager();
// engine = manager.getEngineByName("js");
// ScriptEngineManager manager = new ScriptEngineManager();
// List<ScriptEngineFactory> factories = manager.getEngineFactories();
// for (ScriptEngineFactory factory : factories) {
// System.out.println("Name : " + factory.getEngineName());
// System.out.println("Version : " + factory.getEngineVersion());
// System.out.println("Language name : " + factory.getLanguageName());
// System.out.println("Language version : " + factory.getLanguageVersion());
// System.out.println("Extensions : " + factory.getExtensions());
// System.out.println("Mime types : " + factory.getMimeTypes());
// System.out.println("Names : " + factory.getNames());
//
// }
}
public String eval(String line) {
throw new UnsupportedOperationException();
// final ScriptContext context = engine.getContext();
// final StringWriter sw = new StringWriter();
// context.setWriter(new PrintWriter(sw));
// engine.eval(line, context);
// final String result = sw.toString();
// return result;
}
}

View File

@ -1,71 +0,0 @@
/* ========================================================================
* 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.command;
import java.util.Collections;
import net.sourceforge.plantuml.UmlDiagram;
import net.sourceforge.plantuml.regex.IRegex;
import net.sourceforge.plantuml.regex.RegexConcat;
import net.sourceforge.plantuml.regex.RegexLeaf;
import net.sourceforge.plantuml.regex.RegexResult;
import net.sourceforge.plantuml.utils.LineLocation;
public class CommandAffineTransform extends SingleLineCommand2<UmlDiagram> {
public static final CommandAffineTransform ME = new CommandAffineTransform();
private CommandAffineTransform() {
super(getRegexConcat());
}
static IRegex getRegexConcat() {
return RegexConcat.build(CommandAffineTransform.class.getName(), RegexLeaf.start(), //
new RegexLeaf("!transformation"), //
RegexLeaf.spaceOneOrMore(), //
new RegexLeaf("ANIMATION", "([^{}]*)"), RegexLeaf.end()); //
}
@Override
protected CommandExecutionResult executeArg(UmlDiagram diagram, LineLocation location, RegexResult arg) {
final CharSequence value = arg.get("ANIMATION", 0);
// ::comment when __CORE__
diagram.setAnimation(Collections.singletonList(value));
// ::done
return CommandExecutionResult.ok();
}
}

View File

@ -1,60 +0,0 @@
/* ========================================================================
* 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.command;
import net.sourceforge.plantuml.TitledDiagram;
import net.sourceforge.plantuml.utils.BlocLines;
public class CommandAffineTransformMultiline extends CommandMultilines<TitledDiagram> {
public static final CommandAffineTransformMultiline ME = new CommandAffineTransformMultiline();
private CommandAffineTransformMultiline() {
super("^!transformation[%s]+\\{[%s]*$");
}
@Override
public String getPatternEnd() {
return "^[%s]*!\\}[%s]*$";
}
public CommandExecutionResult execute(final TitledDiagram diagram, BlocLines lines) {
// lines = lines.subExtract(1, 1);
// diagram.setAnimation(lines);
return CommandExecutionResult.error("Not yet implemented");
}
}

View File

@ -73,8 +73,6 @@ public final class CommonCommands {
cmds.add(CommandScaleMaxWidth.ME);
cmds.add(CommandScaleMaxHeight.ME);
cmds.add(CommandScaleMaxWidthAndHeight.ME);
cmds.add(CommandAffineTransform.ME);
cmds.add(CommandAffineTransformMultiline.ME);
final CommandFactorySprite factorySpriteCommand = new CommandFactorySprite();
cmds.add(factorySpriteCommand.createMultiLine(false));
cmds.add(factorySpriteCommand.createSingleLine());

View File

@ -1,829 +0,0 @@
package net.sourceforge.plantuml.jasic;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/*
Jasic uses the MIT License:
Copyright (c) 2010 Robert Nystrom
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
/*
* This defines a single class that contains an entire interpreter for a language very similar to the original BASIC.
* Everything is here (albeit in very simplified form): tokenizing, parsing, and interpretation. The file is organized
* in phases, with each appearing roughly in the order that they occur when a program is run. You should be able to read
* this top-down to walk through the entire process of loading and running a program.
*
* Jasic language syntax ---------------------
*
* Comments start with ' and proceed to the end of the line:
*
* print "hi there" ' this is a comment
*
* Numbers and strings are supported. Strings should be in "double quotes", and only positive integers can be parsed
* (though numbers are double internally).
*
* Variables are identified by name which must start with a letter and can contain letters or numbers. Case is
* significant for names and keywords.
*
* Each statement is on its own line. Optionally, a line may have a label before the statement. A label is a name that
* ends with a colon:
*
* foo:
*
*
* The following statements are supported:
*
* <name> = <expression> Evaluates the expression and assigns the result to the given named variable. All variables are
* globally scoped.
*
* pi = (314159 / 10000)
*
* print <expression> Evaluates the expression and prints the result.
*
* print "hello, " + "world"
*
* input <name> Reads in a line of input from the user and stores it in the variable with the given name.
*
* input guess
*
* goto <label> Jumps to the statement after the label with the given name.
*
* goto loop
*
* if <expression> then <label> Evaluates the expression. If it evaluates to a non-zero number, then jumps to the
* statement after the given label.
*
* if a < b then dosomething
*
*
* The following expressions are supported:
*
* <expression> = <expression> Evaluates to 1 if the two expressions are equal, 0 otherwise.
*
* <expression> + <expression> If the left-hand expression is a number, then adds the two expressions, otherwise
* concatenates the two strings.
*
* <expression> - <expression> <expression> * <expression> <expression> / <expression> <expression> < <expression>
* <expression> > <expression> You can figure it out.
*
* <name> A name in an expression simply returns the value of the variable with that name. If the variable was never
* set, it defaults to 0.
*
* All binary operators have the same precedence. Sorry, I had to cut corners somewhere.
*
* To keep things simple, I've omitted some stuff or hacked things a bit. When possible, I'll leave a "HACK" note there
* explaining what and why. If you make your own interpreter, you'll want to address those.
*
* @author Bob Nystrom
*/
public class Jasic {
// ::remove folder when __HAXE__
// ::remove folder when __CORE__
// ::remove folder when __MIT__ or __EPL__ or __BSD__ or __ASL__ or __LGPL__
// Tokenizing (lexing) -----------------------------------------------------
/**
* This function takes a script as a string of characters and chunks it into a
* sequence of tokens. Each token is a meaningful unit of program, like a
* variable name, a number, a string, or an operator.
*/
private static List<Token> tokenize(String source) {
List<Token> tokens = new ArrayList<>();
String token = "";
TokenizeState state = TokenizeState.DEFAULT;
// Many tokens are a single character, like operators and ().
String charTokens = "\n=+-*/<>()";
TokenType[] tokenTypes = { TokenType.LINE, TokenType.EQUALS, TokenType.OPERATOR, TokenType.OPERATOR,
TokenType.OPERATOR, TokenType.OPERATOR, TokenType.OPERATOR, TokenType.OPERATOR, TokenType.LEFT_PAREN,
TokenType.RIGHT_PAREN };
// Scan through the code one character at a time, building up the list
// of tokens.
for (int i = 0; i < source.length(); i++) {
char c = source.charAt(i);
switch (state) {
case DEFAULT:
if (charTokens.indexOf(c) != -1) {
tokens.add(new Token(Character.toString(c), tokenTypes[charTokens.indexOf(c)]));
} else if (Character.isLetter(c)) {
token += c;
state = TokenizeState.WORD;
} else if (Character.isDigit(c)) {
token += c;
state = TokenizeState.NUMBER;
} else if (c == '"') {
state = TokenizeState.STRING;
} else if (c == '\'') {
state = TokenizeState.COMMENT;
}
break;
case WORD:
if (Character.isLetterOrDigit(c)) {
token += c;
} else if (c == ':') {
tokens.add(new Token(token, TokenType.LABEL));
token = "";
state = TokenizeState.DEFAULT;
} else {
tokens.add(new Token(token, TokenType.WORD));
token = "";
state = TokenizeState.DEFAULT;
i--; // Reprocess this character in the default state.
}
break;
case NUMBER:
// HACK: Negative numbers and floating points aren't supported.
// To get a negative number, just do 0 - <your number>.
// To get a floating point, divide.
if (Character.isDigit(c)) {
token += c;
} else {
tokens.add(new Token(token, TokenType.NUMBER));
token = "";
state = TokenizeState.DEFAULT;
i--; // Reprocess this character in the default state.
}
break;
case STRING:
if (c == '"') {
tokens.add(new Token(token, TokenType.STRING));
token = "";
state = TokenizeState.DEFAULT;
} else {
token += c;
}
break;
case COMMENT:
if (c == '\n') {
state = TokenizeState.DEFAULT;
}
break;
}
}
// HACK: Silently ignore any in-progress token when we run out of
// characters. This means that, for example, if a script has a string
// that's missing the closing ", it will just ditch it.
return tokens;
}
// Token data --------------------------------------------------------------
/**
* This defines the different kinds of tokens or meaningful chunks of code that
* the parser knows how to consume. These let us distinguish, for example,
* between a string "foo" and a variable named "foo".
*
* HACK: A typical tokenizer would actually have unique token types for each
* keyword (print, goto, etc.) so that the parser doesn't have to look at the
* names, but Jasic is a little more crude.
*/
private enum TokenType {
WORD, NUMBER, STRING, LABEL, LINE, EQUALS, OPERATOR, LEFT_PAREN, RIGHT_PAREN, EOF
}
/**
* This is a single meaningful chunk of code. It is created by the tokenizer and
* consumed by the parser.
*/
private static class Token {
public Token(String text, TokenType type) {
this.text = text;
this.type = type;
}
public final String text;
public final TokenType type;
}
/**
* This defines the different states the tokenizer can be in while it's scanning
* through the source code. Tokenizers are state machines, which means the only
* data they need to store is where they are in the source code and this one
* "state" or mode value.
*
* One of the main differences between tokenizing and parsing is this
* regularity. Because the tokenizer stores only this one state value, it can't
* handle nesting (which would require also storing a number to identify how
* deeply nested you are). The parser is able to handle that.
*/
private enum TokenizeState {
DEFAULT, WORD, NUMBER, STRING, COMMENT
}
// Parsing -----------------------------------------------------------------
/**
* This defines the Jasic parser. The parser takes in a sequence of tokens and
* generates an abstract syntax tree. This is the nested data structure that
* represents the series of statements, and the expressions (which can nest
* arbitrarily deeply) that they evaluate. In technical terms, what we have is a
* recursive descent parser, the simplest kind to hand-write.
*
* As a side-effect, this phase also stores off the line numbers for each label
* in the program. It's a bit gross, but it works.
*/
private class Parser {
public Parser(List<Token> tokens) {
this.tokens = tokens;
position = 0;
}
/**
* The top-level function to start parsing. This will keep consuming tokens and
* routing to the other parse functions for the different grammar syntax until
* we run out of code to parse.
*
* @param labels A map of label names to statement indexes. The parser will fill
* this in as it scans the code.
* @return The list of parsed statements.
*/
public List<Statement> parse(Map<String, Integer> labels) {
List<Statement> statements = new ArrayList<>();
while (true) {
// Ignore empty lines.
while (match(TokenType.LINE))
;
if (match(TokenType.LABEL)) {
// Mark the index of the statement after the label.
labels.put(last(1).text, statements.size());
} else if (match(TokenType.WORD, TokenType.EQUALS)) {
String name = last(2).text;
Expression value = expression();
statements.add(new AssignStatement(name, value));
} else if (match("print")) {
statements.add(new PrintStatement(expression()));
} else if (match("input")) {
statements.add(new InputStatement(consume(TokenType.WORD).text));
} else if (match("goto")) {
statements.add(new GotoStatement(consume(TokenType.WORD).text));
} else if (match("if")) {
Expression condition = expression();
consume("then");
String label = consume(TokenType.WORD).text;
statements.add(new IfThenStatement(condition, label));
} else
break; // Unexpected token (likely EOF), so end.
}
return statements;
}
// The following functions each represent one grammatical part of the
// language. If this parsed English, these functions would be named like
// noun() and verb().
/**
* Parses a single expression. Recursive descent parsers start with the
* lowest-precedent term and moves towards higher precedence. For Jasic, binary
* operators (+, -, etc.) are the lowest.
*
* @return The parsed expression.
*/
private Expression expression() {
return operator();
}
/**
* Parses a series of binary operator expressions into a single expression. In
* Jasic, all operators have the same predecence and associate left-to-right.
* That means it will interpret: 1 + 2 * 3 - 4 / 5 like: ((((1 + 2) * 3) - 4) /
* 5)
*
* It works by building the expression tree one at a time. So, given this code:
* 1 + 2 * 3, this will:
*
* 1. Parse (1) as an atomic expression. 2. See the (+) and start a new operator
* expression. 3. Parse (2) as an atomic expression. 4. Build a (1 + 2)
* expression and replace (1) with it. 5. See the (*) and start a new operator
* expression. 6. Parse (3) as an atomic expression. 7. Build a ((1 + 2) * 3)
* expression and replace (1 + 2) with it. 8. Return the last expression built.
*
* @return The parsed expression.
*/
private Expression operator() {
Expression expression = atomic();
// Keep building operator expressions as long as we have operators.
while (match(TokenType.OPERATOR) || match(TokenType.EQUALS)) {
char operator = last(1).text.charAt(0);
Expression right = atomic();
expression = new OperatorExpression(expression, operator, right);
}
return expression;
}
/**
* Parses an "atomic" expression. This is the highest level of precedence and
* contains single literal tokens like 123 and "foo", as well as parenthesized
* expressions.
*
* @return The parsed expression.
*/
private Expression atomic() {
if (match(TokenType.WORD)) {
// A word is a reference to a variable.
return new VariableExpression(last(1).text);
} else if (match(TokenType.NUMBER)) {
return new NumberValue(Double.parseDouble(last(1).text));
} else if (match(TokenType.STRING)) {
return new StringValue(last(1).text);
} else if (match(TokenType.LEFT_PAREN)) {
// The contents of a parenthesized expression can be any
// expression. This lets us "restart" the precedence cascade
// so that you can have a lower precedence expression inside
// the parentheses.
Expression expression = expression();
consume(TokenType.RIGHT_PAREN);
return expression;
}
throw new Error("Couldn't parse :(");
}
// The following functions are the core low-level operations that the
// grammar parser is built in terms of. They match and consume tokens in
// the token stream.
/**
* Consumes the next two tokens if they are the given type (in order). Consumes
* no tokens if either check fais.
*
* @param type1 Expected type of the next token.
* @param type2 Expected type of the subsequent token.
* @return True if tokens were consumed.
*/
private boolean match(TokenType type1, TokenType type2) {
if (get(0).type != type1)
return false;
if (get(1).type != type2)
return false;
position += 2;
return true;
}
/**
* Consumes the next token if it's the given type.
*
* @param type Expected type of the next token.
* @return True if the token was consumed.
*/
private boolean match(TokenType type) {
if (get(0).type != type)
return false;
position++;
return true;
}
/**
* Consumes the next token if it's a word token with the given name.
*
* @param name Expected name of the next word token.
* @return True if the token was consumed.
*/
private boolean match(String name) {
if (get(0).type != TokenType.WORD)
return false;
if (!get(0).text.equals(name))
return false;
position++;
return true;
}
/**
* Consumes the next token if it's the given type. If not, throws an exception.
* This is for cases where the parser demands a token of a certain type in a
* certain position, for example a matching ) after an opening (.
*
* @param type Expected type of the next token.
* @return The consumed token.
*/
private Token consume(TokenType type) {
if (get(0).type != type)
throw new Error("Expected " + type + ".");
return tokens.get(position++);
}
/**
* Consumes the next token if it's a word with the given name. If not, throws an
* exception.
*
* @param name Expected name of the next word token.
* @return The consumed token.
*/
private Token consume(String name) {
if (!match(name))
throw new Error("Expected " + name + ".");
return last(1);
}
/**
* Gets a previously consumed token, indexing backwards. last(1) will be the
* token just consumed, last(2) the one before that, etc.
*
* @param offset How far back in the token stream to look.
* @return The consumed token.
*/
private Token last(int offset) {
return tokens.get(position - offset);
}
/**
* Gets an unconsumed token, indexing forward. get(0) will be the next token to
* be consumed, get(1) the one after that, etc.
*
* @param offset How far forward in the token stream to look.
* @return The yet-to-be-consumed token.
*/
private Token get(int offset) {
if (position + offset >= tokens.size()) {
return new Token("", TokenType.EOF);
}
return tokens.get(position + offset);
}
private final List<Token> tokens;
private int position;
}
// Abstract syntax tree (AST) ----------------------------------------------
// These classes define the syntax tree data structures. This is how code is
// represented internally in a way that's easy for the interpreter to
// understand.
//
// HACK: Unlike most real compilers or interpreters, the logic to execute
// the code is baked directly into these classes. Typically, it would be
// separated out so that the AST us just a static data structure.
/**
* Base interface for a Jasic statement. The different supported statement types
* like "print" and "goto" implement this.
*/
public interface Statement {
/**
* Statements implement this to actually perform whatever behavior the statement
* causes. "print" statements will display text here, "goto" statements will
* change the current statement, etc.
*/
void execute();
}
/**
* Base interface for an expression. An expression is like a statement except
* that it also returns a value when executed. Expressions do not appear at the
* top level in Jasic programs, but are used in many statements. For example,
* the value printed by a "print" statement is an expression. Unlike statements,
* expressions can nest.
*/
public interface Expression {
/**
* Expression classes implement this to evaluate the expression and return the
* value.
*
* @return The value of the calculated expression.
*/
Value evaluate();
}
/**
* A "print" statement evaluates an expression, converts the result to a string,
* and displays it to the user.
*/
public class PrintStatement implements Statement {
public PrintStatement(Expression expression) {
this.expression = expression;
}
public void execute() {
System.out.println(expression.evaluate().toString());
}
private final Expression expression;
}
/**
* An "input" statement reads input from the user and stores it in a variable.
*/
public class InputStatement implements Statement {
public InputStatement(String name) {
this.name = name;
}
public void execute() {
try {
String input = lineIn.readLine();
// Store it as a number if possible, otherwise use a string.
try {
double value = Double.parseDouble(input);
variables.put(name, new NumberValue(value));
} catch (NumberFormatException e) {
variables.put(name, new StringValue(input));
}
} catch (IOException e1) {
// HACK: Just ignore the problem.
}
}
private final String name;
}
/**
* An assignment statement evaluates an expression and stores the result in a
* variable.
*/
public class AssignStatement implements Statement {
public AssignStatement(String name, Expression value) {
this.name = name;
this.value = value;
}
public void execute() {
variables.put(name, value.evaluate());
}
private final String name;
private final Expression value;
}
/**
* A "goto" statement jumps execution to another place in the program.
*/
public class GotoStatement implements Statement {
public GotoStatement(String label) {
this.label = label;
}
public void execute() {
if (labels.containsKey(label)) {
currentStatement = labels.get(label).intValue();
}
}
private final String label;
}
/**
* An if then statement jumps execution to another place in the program, but
* only if an expression evaluates to something other than 0.
*/
public class IfThenStatement implements Statement {
public IfThenStatement(Expression condition, String label) {
this.condition = condition;
this.label = label;
}
public void execute() {
if (labels.containsKey(label)) {
double value = condition.evaluate().toNumber();
if (value != 0) {
currentStatement = labels.get(label).intValue();
}
}
}
private final Expression condition;
private final String label;
}
/**
* A variable expression evaluates to the current value stored in that variable.
*/
public class VariableExpression implements Expression {
public VariableExpression(String name) {
this.name = name;
}
public Value evaluate() {
if (variables.containsKey(name)) {
return variables.get(name);
}
return new NumberValue(0);
}
private final String name;
}
/**
* An operator expression evaluates two expressions and then performs some
* arithmetic operation on the results.
*/
public class OperatorExpression implements Expression {
public OperatorExpression(Expression left, char operator, Expression right) {
this.left = left;
this.operator = operator;
this.right = right;
}
public Value evaluate() {
Value leftVal = left.evaluate();
Value rightVal = right.evaluate();
switch (operator) {
case '=':
// Coerce to the left argument's type, then compare.
if (leftVal instanceof NumberValue) {
return new NumberValue((leftVal.toNumber() == rightVal.toNumber()) ? 1 : 0);
} else {
return new NumberValue(leftVal.toString().equals(rightVal.toString()) ? 1 : 0);
}
case '+':
// Addition if the left argument is a number, otherwise do
// string concatenation.
if (leftVal instanceof NumberValue) {
return new NumberValue(leftVal.toNumber() + rightVal.toNumber());
} else {
return new StringValue(leftVal.toString() + rightVal.toString());
}
case '-':
return new NumberValue(leftVal.toNumber() - rightVal.toNumber());
case '*':
return new NumberValue(leftVal.toNumber() * rightVal.toNumber());
case '/':
return new NumberValue(leftVal.toNumber() / rightVal.toNumber());
case '<':
// Coerce to the left argument's type, then compare.
if (leftVal instanceof NumberValue) {
return new NumberValue((leftVal.toNumber() < rightVal.toNumber()) ? 1 : 0);
} else {
return new NumberValue((leftVal.toString().compareTo(rightVal.toString()) < 0) ? 1 : 0);
}
case '>':
// Coerce to the left argument's type, then compare.
if (leftVal instanceof NumberValue) {
return new NumberValue((leftVal.toNumber() > rightVal.toNumber()) ? 1 : 0);
} else {
return new NumberValue((leftVal.toString().compareTo(rightVal.toString()) > 0) ? 1 : 0);
}
}
throw new Error("Unknown operator.");
}
private final Expression left;
private final char operator;
private final Expression right;
}
// Value types -------------------------------------------------------------
/**
* This is the base interface for a value. Values are the data that the
* interpreter processes. They are what gets stored in variables, printed, and
* operated on.
*
* There is an implementation of this interface for each of the different
* primitive types (really just double and string) that Jasic supports. Wrapping
* them in a single Value interface lets Jasic be dynamically-typed and convert
* between different representations as needed.
*
* Note that Value extends Expression. This is a bit of a hack, but it lets us
* use values (which are typically only ever seen by the interpreter and not the
* parser) as both runtime values, and as object representing literals in code.
*/
public interface Value extends Expression {
/**
* Value types override this to convert themselves to a string representation.
*/
String toString();
/**
* Value types override this to convert themselves to a numeric representation.
*/
double toNumber();
}
/**
* A numeric value. Jasic uses doubles internally for all numbers.
*/
public class NumberValue implements Value {
public NumberValue(double value) {
this.value = value;
}
@Override
public String toString() {
return Double.toString(value);
}
public double toNumber() {
return value;
}
public Value evaluate() {
return this;
}
private final double value;
}
/**
* A string value.
*/
public class StringValue implements Value {
public StringValue(String value) {
this.value = value;
}
@Override
public String toString() {
return value;
}
public double toNumber() {
return Double.parseDouble(value);
}
public Value evaluate() {
return this;
}
private final String value;
}
// Interpreter -------------------------------------------------------------
/**
* Constructs a new Jasic instance. The instance stores the global state of the
* interpreter such as the values of all of the variables and the current
* statement.
*/
public Jasic() {
variables = new HashMap<String, Value>();
labels = new HashMap<String, Integer>();
InputStreamReader converter = new InputStreamReader(System.in);
lineIn = new BufferedReader(converter);
}
/**
* This is where the magic happens. This runs the code through the parsing
* pipeline to generate the AST. Then it executes each statement. It keeps track
* of the current line in a member variable that the statement objects have
* access to. This lets "goto" and "if then" do flow control by simply setting
* the index of the current statement.
*
* In an interpreter that didn't mix the interpretation logic in with the AST
* node classes, this would be doing a lot more work.
*
* @param source A string containing the source code of a .jas script to
* interpret.
*/
public void interpret(String source) {
// Tokenize.
List<Token> tokens = tokenize(source);
// Parse.
Parser parser = new Parser(tokens);
List<Statement> statements = parser.parse(labels);
// Interpret until we're done.
currentStatement = 0;
while (currentStatement < statements.size()) {
int thisStatement = currentStatement;
currentStatement++;
statements.get(thisStatement).execute();
}
}
private final Map<String, Value> variables;
private final Map<String, Integer> labels;
private final BufferedReader lineIn;
private int currentStatement;
}

View File

@ -37,7 +37,6 @@ package net.sourceforge.plantuml.klimt.drawing.g2d;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
@ -50,7 +49,6 @@ import java.util.Objects;
import java.util.Set;
import net.sourceforge.plantuml.FileFormat;
import net.sourceforge.plantuml.anim.AffineTransformation;
import net.sourceforge.plantuml.klimt.UAntiAliasing;
import net.sourceforge.plantuml.klimt.UChange;
import net.sourceforge.plantuml.klimt.UClip;
@ -88,10 +86,6 @@ public class UGraphicG2d extends AbstractUGraphic<Graphics2D> implements EnsureV
private List<Url> urls = new ArrayList<>();
private Set<Url> allUrls = new HashSet<>();
// ::comment when __CORE__
private final boolean hasAffineTransform;
// ::done
public final Set<Url> getAllUrlsEncountered() {
return Collections.unmodifiableSet(allUrls);
}
@ -114,9 +108,6 @@ public class UGraphicG2d extends AbstractUGraphic<Graphics2D> implements EnsureV
private UGraphicG2d(UGraphicG2d other) {
super(other.getStringBounder());
copy(other);
// ::comment when __CORE__
this.hasAffineTransform = other.hasAffineTransform;
// ::done
this.dpiFactor = other.dpiFactor;
this.bufferedImage = other.bufferedImage;
this.urls = other.urls;
@ -128,20 +119,11 @@ public class UGraphicG2d extends AbstractUGraphic<Graphics2D> implements EnsureV
public UGraphicG2d(HColor defaultBackground, ColorMapper colorMapper, StringBounder stringBounder, Graphics2D g2d,
double dpiFactor, FileFormat format) {
// ::revert when __CORE__
this(defaultBackground, colorMapper, stringBounder, g2d, dpiFactor, 0, 0, format, null);
// this(defaultBackground, colorMapper, stringBounder, g2d, dpiFactor, 0, 0,
// format);
// ::done
this(defaultBackground, colorMapper, stringBounder, g2d, dpiFactor, 0, 0, format);
}
// ::revert when __CORE__
public UGraphicG2d(HColor defaultBackground, ColorMapper colorMapper, StringBounder stringBounder, Graphics2D g2d,
double dpiFactor, double dx, double dy, FileFormat format, AffineTransformation affineTransform) {
// public UGraphicG2d(HColor defaultBackground, ColorMapper colorMapper,
// StringBounder stringBounder, Graphics2D g2d,
// double dpiFactor, double dx, double dy, FileFormat format) {
// ::done
double dpiFactor, double dx, double dy, FileFormat format) {
super(stringBounder);
copy(defaultBackground, colorMapper, g2d);
this.format = format;
@ -149,21 +131,13 @@ public class UGraphicG2d extends AbstractUGraphic<Graphics2D> implements EnsureV
if (dpiFactor != 1.0)
g2d.scale(dpiFactor, dpiFactor);
// ::comment when __CORE__
this.hasAffineTransform = affineTransform != null;
if (this.hasAffineTransform) {
if (dx != 0 || dy != 0)
getGraphicObject().transform(AffineTransform.getTranslateInstance(dx, dy));
getGraphicObject().transform(affineTransform.getAffineTransform());
}
// ::done
register(dpiFactor);
}
private void register(double dpiFactor) {
registerDriver(URectangle.class, new DriverRectangleG2d(dpiFactor, this));
// ::comment when __CORE__
if (this.hasAffineTransform || dpiFactor != 1.0)
if (dpiFactor != 1.0)
registerDriver(UText.class, new DriverTextAsPathG2d(this, getStringBounder()));
else
// ::done

View File

@ -1,493 +0,0 @@
/*
* MJPEGGenerator.java
*
* Created on April 17, 2006, 11:48 PM
*
* To change this template, choose Tools | Options and locate the template under
* the Source Creation and Management node. Right-click the template and choose
* Open. You can then make changes to the template in the Source Editor.
*/
package net.sourceforge.plantuml.mjpeg;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Arrays;
import net.sourceforge.plantuml.security.SFile;
import net.sourceforge.plantuml.security.SImageIO;
/**
*
* @author monceaux
*/
public class MJPEGGenerator {
// ::remove folder when __HAXE__
// ::remove folder when __CORE__
/*
* Info needed for MJPEG AVI
*
* - size of file minus "RIFF & 4 byte file size"
*
*/
int width = 0;
int height = 0;
double framerate = 0;
int numFrames = 0;
SFile aviFile = null;
FileOutputStream aviOutput = null;
FileChannel aviChannel = null;
long riffOffset = 0;
long aviMovieOffset = 0;
AVIIndexList indexlist = null;
/** Creates a new instance of MJPEGGenerator */
public MJPEGGenerator(SFile aviFile, int width, int height, double framerate, int numFrames) throws IOException {
this.aviFile = aviFile;
this.width = width;
this.height = height;
this.framerate = framerate;
this.numFrames = numFrames;
aviOutput = aviFile.createFileOutputStream();
aviChannel = aviOutput.getChannel();
RIFFHeader rh = new RIFFHeader();
aviOutput.write(rh.toBytes());
aviOutput.write(new AVIMainHeader().toBytes());
aviOutput.write(new AVIStreamList().toBytes());
aviOutput.write(new AVIStreamHeader().toBytes());
aviOutput.write(new AVIStreamFormat().toBytes());
aviOutput.write(new AVIJunk().toBytes());
aviMovieOffset = aviChannel.position();
aviOutput.write(new AVIMovieList().toBytes());
indexlist = new AVIIndexList();
}
public void addImage(Image image) throws IOException {
byte[] fcc = new byte[] { '0', '0', 'd', 'b' };
byte[] imagedata = writeImageToBytes(image);
int useLength = imagedata.length;
long position = aviChannel.position();
int extra = (useLength + (int) position) % 4;
if (extra > 0)
useLength = useLength + extra;
indexlist.addAVIIndex((int) position, useLength);
aviOutput.write(fcc);
aviOutput.write(intBytes(swapInt(useLength)));
aviOutput.write(imagedata);
if (extra > 0) {
for (int i = 0; i < extra; i++)
aviOutput.write(0);
}
imagedata = null;
}
public void finishAVI() throws IOException {
byte[] indexlistBytes = indexlist.toBytes();
aviOutput.write(indexlistBytes);
aviOutput.close();
long size = aviFile.length();
RandomAccessFile raf = new RandomAccessFile(aviFile.conv(), "rw");
raf.seek(4);
raf.write(intBytes(swapInt((int) size - 8)));
raf.seek(aviMovieOffset + 4);
raf.write(intBytes(swapInt((int) (size - 8 - aviMovieOffset - indexlistBytes.length))));
raf.close();
}
// public void writeAVI(File file) throws Exception
// {
// OutputStream os = SecurityUtils.FileOutputStream(file);
//
// // RIFFHeader
// // AVIMainHeader
// // AVIStreamList
// // AVIStreamHeader
// // AVIStreamFormat
// // write 00db and image bytes...
// }
public static int swapInt(int v) {
return (v >>> 24) | (v << 24) | ((v << 8) & 0x00FF0000) | ((v >> 8) & 0x0000FF00);
}
public static short swapShort(short v) {
return (short) ((v >>> 8) | (v << 8));
}
public static byte[] intBytes(int i) {
byte[] b = new byte[4];
b[0] = (byte) (i >>> 24);
b[1] = (byte) ((i >>> 16) & 0x000000FF);
b[2] = (byte) ((i >>> 8) & 0x000000FF);
b[3] = (byte) (i & 0x000000FF);
return b;
}
public static byte[] shortBytes(short i) {
byte[] b = new byte[2];
b[0] = (byte) (i >>> 8);
b[1] = (byte) (i & 0x000000FF);
return b;
}
private class RIFFHeader {
public byte[] fcc = new byte[] { 'R', 'I', 'F', 'F' };
public int fileSize = 0;
public byte[] fcc2 = new byte[] { 'A', 'V', 'I', ' ' };
public byte[] fcc3 = new byte[] { 'L', 'I', 'S', 'T' };
public int listSize = 200;
public byte[] fcc4 = new byte[] { 'h', 'd', 'r', 'l' };
public RIFFHeader() {
}
public byte[] toBytes() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write(fcc);
baos.write(intBytes(swapInt(fileSize)));
baos.write(fcc2);
baos.write(fcc3);
baos.write(intBytes(swapInt(listSize)));
baos.write(fcc4);
baos.close();
return baos.toByteArray();
}
}
private class AVIMainHeader {
/*
*
* FOURCC fcc; DWORD cb; DWORD dwMicroSecPerFrame; DWORD dwMaxBytesPerSec; DWORD
* dwPaddingGranularity; DWORD dwFlags; DWORD dwTotalFrames; DWORD
* dwInitialFrames; DWORD dwStreams; DWORD dwSuggestedBufferSize; DWORD dwWidth;
* DWORD dwHeight; DWORD dwReserved[4];
*/
public byte[] fcc = new byte[] { 'a', 'v', 'i', 'h' };
public int cb = 56;
public int dwMicroSecPerFrame = 0; // (1 / frames per sec) * 1,000,000
public int dwMaxBytesPerSec = 10000000;
public int dwPaddingGranularity = 0;
public int dwFlags = 65552;
public int dwTotalFrames = 0; // replace with correct value
public int dwInitialFrames = 0;
public int dwStreams = 1;
public int dwSuggestedBufferSize = 0;
public int dwWidth = 0; // replace with correct value
public int dwHeight = 0; // replace with correct value
public int[] dwReserved = new int[4];
public AVIMainHeader() {
dwMicroSecPerFrame = (int) ((1.0 / framerate) * 1000000.0);
dwWidth = width;
dwHeight = height;
dwTotalFrames = numFrames;
}
public byte[] toBytes() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write(fcc);
baos.write(intBytes(swapInt(cb)));
baos.write(intBytes(swapInt(dwMicroSecPerFrame)));
baos.write(intBytes(swapInt(dwMaxBytesPerSec)));
baos.write(intBytes(swapInt(dwPaddingGranularity)));
baos.write(intBytes(swapInt(dwFlags)));
baos.write(intBytes(swapInt(dwTotalFrames)));
baos.write(intBytes(swapInt(dwInitialFrames)));
baos.write(intBytes(swapInt(dwStreams)));
baos.write(intBytes(swapInt(dwSuggestedBufferSize)));
baos.write(intBytes(swapInt(dwWidth)));
baos.write(intBytes(swapInt(dwHeight)));
baos.write(intBytes(swapInt(dwReserved[0])));
baos.write(intBytes(swapInt(dwReserved[1])));
baos.write(intBytes(swapInt(dwReserved[2])));
baos.write(intBytes(swapInt(dwReserved[3])));
baos.close();
return baos.toByteArray();
}
}
private class AVIStreamList {
public byte[] fcc = new byte[] { 'L', 'I', 'S', 'T' };
public int size = 124;
public byte[] fcc2 = new byte[] { 's', 't', 'r', 'l' };
public AVIStreamList() {
}
public byte[] toBytes() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write(fcc);
baos.write(intBytes(swapInt(size)));
baos.write(fcc2);
baos.close();
return baos.toByteArray();
}
}
private class AVIStreamHeader {
/*
* FOURCC fcc; DWORD cb; FOURCC fccType; FOURCC fccHandler; DWORD dwFlags; WORD
* wPriority; WORD wLanguage; DWORD dwInitialFrames; DWORD dwScale; DWORD
* dwRate; DWORD dwStart; DWORD dwLength; DWORD dwSuggestedBufferSize; DWORD
* dwQuality; DWORD dwSampleSize; struct { short int left; short int top; short
* int right; short int bottom; } rcFrame;
*/
public byte[] fcc = new byte[] { 's', 't', 'r', 'h' };
public int cb = 64;
public byte[] fccType = new byte[] { 'v', 'i', 'd', 's' };
public byte[] fccHandler = new byte[] { 'M', 'J', 'P', 'G' };
public int dwFlags = 0;
public short wPriority = 0;
public short wLanguage = 0;
public int dwInitialFrames = 0;
public int dwScale = 0; // microseconds per frame
public int dwRate = 1000000; // dwRate / dwScale = frame rate
public int dwStart = 0;
public int dwLength = 0; // num frames
public int dwSuggestedBufferSize = 0;
public int dwQuality = -1;
public int dwSampleSize = 0;
public int left = 0;
public int top = 0;
public int right = 0;
public int bottom = 0;
public AVIStreamHeader() {
dwScale = (int) ((1.0 / framerate) * 1000000.0);
dwLength = numFrames;
}
public byte[] toBytes() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write(fcc);
baos.write(intBytes(swapInt(cb)));
baos.write(fccType);
baos.write(fccHandler);
baos.write(intBytes(swapInt(dwFlags)));
baos.write(shortBytes(swapShort(wPriority)));
baos.write(shortBytes(swapShort(wLanguage)));
baos.write(intBytes(swapInt(dwInitialFrames)));
baos.write(intBytes(swapInt(dwScale)));
baos.write(intBytes(swapInt(dwRate)));
baos.write(intBytes(swapInt(dwStart)));
baos.write(intBytes(swapInt(dwLength)));
baos.write(intBytes(swapInt(dwSuggestedBufferSize)));
baos.write(intBytes(swapInt(dwQuality)));
baos.write(intBytes(swapInt(dwSampleSize)));
baos.write(intBytes(swapInt(left)));
baos.write(intBytes(swapInt(top)));
baos.write(intBytes(swapInt(right)));
baos.write(intBytes(swapInt(bottom)));
baos.close();
return baos.toByteArray();
}
}
private class AVIStreamFormat {
/*
* FOURCC fcc; DWORD cb; DWORD biSize; LONG biWidth; LONG biHeight; WORD
* biPlanes; WORD biBitCount; DWORD biCompression; DWORD biSizeImage; LONG
* biXPelsPerMeter; LONG biYPelsPerMeter; DWORD biClrUsed; DWORD biClrImportant;
*/
public byte[] fcc = new byte[] { 's', 't', 'r', 'f' };
public int cb = 40;
public int biSize = 40; // same as cb
public int biWidth = 0;
public int biHeight = 0;
public short biPlanes = 1;
public short biBitCount = 24;
public byte[] biCompression = new byte[] { 'M', 'J', 'P', 'G' };
public int biSizeImage = 0; // width x height in pixels
public int biXPelsPerMeter = 0;
public int biYPelsPerMeter = 0;
public int biClrUsed = 0;
public int biClrImportant = 0;
public AVIStreamFormat() {
biWidth = width;
biHeight = height;
biSizeImage = width * height;
}
public byte[] toBytes() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write(fcc);
baos.write(intBytes(swapInt(cb)));
baos.write(intBytes(swapInt(biSize)));
baos.write(intBytes(swapInt(biWidth)));
baos.write(intBytes(swapInt(biHeight)));
baos.write(shortBytes(swapShort(biPlanes)));
baos.write(shortBytes(swapShort(biBitCount)));
baos.write(biCompression);
baos.write(intBytes(swapInt(biSizeImage)));
baos.write(intBytes(swapInt(biXPelsPerMeter)));
baos.write(intBytes(swapInt(biYPelsPerMeter)));
baos.write(intBytes(swapInt(biClrUsed)));
baos.write(intBytes(swapInt(biClrImportant)));
baos.close();
return baos.toByteArray();
}
}
private class AVIMovieList {
public byte[] fcc = new byte[] { 'L', 'I', 'S', 'T' };
public int listSize = 0;
public byte[] fcc2 = new byte[] { 'm', 'o', 'v', 'i' };
// 00db size jpg image data ...
public AVIMovieList() {
}
public byte[] toBytes() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write(fcc);
baos.write(intBytes(swapInt(listSize)));
baos.write(fcc2);
baos.close();
return baos.toByteArray();
}
}
private class AVIIndexList {
public byte[] fcc = new byte[] { 'i', 'd', 'x', '1' };
public int cb = 0;
public ArrayList ind = new ArrayList();
public AVIIndexList() {
}
public void addAVIIndex(AVIIndex ai) {
ind.add(ai);
}
public void addAVIIndex(int dwOffset, int dwSize) {
ind.add(new AVIIndex(dwOffset, dwSize));
}
public byte[] toBytes() throws IOException {
cb = 16 * ind.size();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write(fcc);
baos.write(intBytes(swapInt(cb)));
for (int i = 0; i < ind.size(); i++) {
AVIIndex in = (AVIIndex) ind.get(i);
baos.write(in.toBytes());
}
baos.close();
return baos.toByteArray();
}
}
private class AVIIndex {
public byte[] fcc = new byte[] { '0', '0', 'd', 'b' };
public int dwFlags = 16;
public int dwOffset = 0;
public int dwSize = 0;
public AVIIndex(int dwOffset, int dwSize) {
this.dwOffset = dwOffset;
this.dwSize = dwSize;
}
public byte[] toBytes() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write(fcc);
baos.write(intBytes(swapInt(dwFlags)));
baos.write(intBytes(swapInt(dwOffset)));
baos.write(intBytes(swapInt(dwSize)));
baos.close();
return baos.toByteArray();
}
}
private class AVIJunk {
public byte[] fcc = new byte[] { 'J', 'U', 'N', 'K' };
public int size = 1808;
public byte[] data = new byte[size];
public AVIJunk() {
Arrays.fill(data, (byte) 0);
}
public byte[] toBytes() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write(fcc);
baos.write(intBytes(swapInt(size)));
baos.write(data);
baos.close();
return baos.toByteArray();
}
}
private byte[] writeImageToBytes(Image image) throws IOException {
BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Graphics2D g = bi.createGraphics();
g.drawImage(image, 0, 0, width, height, null);
SImageIO.write(bi, "jpg", baos);
baos.close();
bi = null;
g = null;
return baos.toByteArray();
}
// public static void main(String[] args) throws Exception {
// double framerate = 12.0;
// double transitionDuration = 1; // seconds
// double slideDuration = 3; // seconds
//
// File photoDir = SecurityUtils.file(args[0]);
// File[] files = photoDir.listFiles(new FilenameFilter() {
// public boolean accept(java.io.File dir, String name) {
// if (StringUtils.goLowerCase(name).endsWith("jpg"))
// return true;
// return false;
// }
// });
//
// int numFrames = (int) (files.length * framerate * (slideDuration + transitionDuration)
// + (transitionDuration * framerate));
// MJPEGGenerator m = new MJPEGGenerator(SecurityUtils.file(args[1]), 640, 480, framerate, numFrames);
// for (int i = 0; i < files.length; i++) {
// System.out.println("processing file " + i);
// ImageIcon ii = new ImageIcon(files[i].getCanonicalPath());
// m.addImage(ii.getImage());
// }
// m.finishAVI();
// }
}