mirror of
https://github.com/octoleo/plantuml.git
synced 2024-11-25 06:17:33 +00:00
refactor: remove unused code
This commit is contained in:
parent
8f5c3379ed
commit
e92dcd400b
2
attic.md
2
attic.md
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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";
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
@ -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());
|
||||
|
@ -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;
|
||||
|
||||
}
|
@ -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
|
||||
|
@ -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();
|
||||
// }
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user