diff options
Diffstat (limited to 'logo/src/xlogo/kernel')
72 files changed, 23210 insertions, 0 deletions
diff --git a/logo/src/xlogo/kernel/Affichage.java b/logo/src/xlogo/kernel/Affichage.java new file mode 100644 index 0000000..7da2120 --- /dev/null +++ b/logo/src/xlogo/kernel/Affichage.java @@ -0,0 +1,207 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +/** + * Title : XLogo Description : XLogo is an interpreter for the Logo programming + * language + * + * @author Loïc Le Coq + */ +package xlogo.kernel; + +import java.util.Stack; +import java.util.HashMap; +import java.awt.event.*; + +import javax.swing.SwingUtilities; + +import xlogo.Application; +import xlogo.messages.async.history.HistoryMessenger; +import xlogo.utils.Utils; +import xlogo.MemoryChecker; +import xlogo.Logo; + +// Ce thread gère l'animation de la tortue pendant l'exécution +/** + * This Thread is responsible of the turtle animation. + * This animation has to be executed in a separated thread, else it will block + * the event dispatcher thread + */ +public class Affichage extends Thread +{ + public static boolean execution_lancee = false; + private boolean pause = false; + private Application cadre; + private StringBuffer instruction; + private Souris souris = new Souris(); + private MemoryChecker memoryChecker = null; + + public Affichage() + { + } + + public Affichage(Application cadre, StringBuffer instruction) + { + this.cadre = cadre; + this.instruction = instruction; + } + + class Souris extends MouseAdapter + { // Si on déplace les Scrollbar pendant + // le dessin + public Souris() + { + } // Ceci permet d'interrompre momentanément l'exécution + + public void mousePressed(MouseEvent e) + { + pause = true; + } + + public void mouseReleased(MouseEvent e) + { + pause = false; + } + } + + public void run() + { + // currentThread().setPriority(Thread.MIN_PRIORITY); + SwingUtilities.invokeLater(new Runnable(){ + public void run() + { + cadre.setCommandLine(false);// la ligne de commandes + // n'est plus active + } + }); + execution_lancee = true; + cadre.getDrawPanel().active_souris(); // On active les événements souris + // sur + // la zone de dessin + cadre.scrollArea.getVerticalScrollBar().addMouseListener(souris); + cadre.scrollArea.getHorizontalScrollBar().addMouseListener(souris); + try + { + cadre.setCar(-1); + cadre.error = false; + Interprete.operande = Interprete.operateur = Interprete.drapeau_ouvrante = false; + cadre.getKernel().getInstructionBuffer().clear(); + Interprete.calcul = new Stack<String>(); + Interprete.nom = new Stack<String>(); + Interprete.locale = new HashMap<String, String>(); + Interprete.en_cours = new Stack<String>(); + memoryChecker = new MemoryChecker(cadre); + memoryChecker.start(); + boolean b = true; + while (b) + { + String st = cadre.getKernel().execute(instruction); + if (!st.equals("")) + throw new LogoError(Logo.messages.getString("error.whattodo") + " " + st + " ?"); + if (Interprete.actionInstruction.length() == 0) + b = false; + else + { + instruction = Interprete.actionInstruction; + Interprete.actionInstruction = new StringBuffer(); + } + } + } + catch (LogoError e) + { + // if (st.equals("siwhile")) st=Logo.messages.getString("tantque"); + while (!Interprete.en_cours.isEmpty() && Interprete.en_cours.peek().equals("(")) + Interprete.en_cours.pop(); + if (!cadre.error & !Interprete.en_cours.isEmpty()) + { + HistoryMessenger.getInstance().dispatchError( + Logo.messages.getString("dans") + " " + Interprete.en_cours.pop() + ", " + + Logo.messages.getString("line") + " " + getLineNumber() + ":\n"); + } + if (!cadre.error) + HistoryMessenger.getInstance().dispatchError(Utils.SortieTexte(e.getMessage()) + "\n"); + abortExecution(); + } + cadre.setCommandLine(true); + if (!cadre.viewer3DVisible()) + cadre.focus_Commande(); + execution_lancee = false; + memoryChecker.kill(); + cadre.error = false; + cadre.scrollArea.getVerticalScrollBar().removeMouseListener(souris); + cadre.scrollArea.getHorizontalScrollBar().removeMouseListener(souris); + } + + private void abortExecution() + { + cadre.focus_Commande(); + cadre.error = true; + Interprete.calcul = new Stack<String>(); + cadre.getKernel().getInstructionBuffer().clear(); + Primitive.stackLoop = new Stack<LoopProperties>(); + } + + private int getLineNumber() + { + String string = Interprete.lineNumber; + // System.out.println("bb"+string+"bb"); + if (string.equals("")) + string = cadre.getKernel().getInstructionBuffer().toString(); + // System.out.println("cc"+string+"cc"); + int id = string.indexOf("\\l"); + if (id != -1) + { + String lineNumber = ""; + int i = id + 2; + char c = string.charAt(i); + while (c != ' ') + { + lineNumber += c; + i++; + c = string.charAt(i); + } + // System.out.println(lineNumber); + return Integer.parseInt(lineNumber); + } + return 1; + } + + protected boolean isOnPause() + { + return pause; + } + + /** + * @param b + * @uml.property name="pause" + */ + public void setPause(boolean b) + { + pause = b; + } +} diff --git a/logo/src/xlogo/kernel/DrawPanel.java b/logo/src/xlogo/kernel/DrawPanel.java new file mode 100644 index 0000000..269a0c0 --- /dev/null +++ b/logo/src/xlogo/kernel/DrawPanel.java @@ -0,0 +1,2917 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +package xlogo.kernel; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.HeadlessException; +import java.awt.Image; +import java.awt.MediaTracker; +import java.awt.Point; + +import javax.swing.JViewport; + +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.Transparency; + +import javax.vecmath.Color3f; + +import java.awt.FontMetrics; +import java.awt.Stroke; +import java.awt.Toolkit; +import java.awt.geom.AffineTransform; +import java.awt.geom.Arc2D; +import java.awt.geom.CubicCurve2D; +import java.awt.geom.GeneralPath; +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; +import java.awt.geom.QuadCurve2D; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.font.TextLayout; +import java.awt.Dimension; +import java.awt.geom.PathIterator; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.awt.image.CropImageFilter; +import java.awt.image.ReplicateScaleFilter; +import java.awt.image.FilteredImageSource; + +import com.sun.j3d.utils.geometry.Text2D; + +import javax.swing.ImageIcon; +import javax.swing.SwingUtilities; + +import java.io.File; +import java.io.IOException; +import java.math.BigDecimal; + +import javax.swing.JPanel; +import javax.imageio.ImageIO; +import javax.media.j3d.*; +import javax.vecmath.Vector3d; +import javax.vecmath.Matrix3d; +import javax.vecmath.Point3d; + +import java.util.Stack; +import java.util.Set; +import java.util.Iterator; +import java.util.StringTokenizer; +import java.util.Vector; +import java.awt.event.*; + +import xlogo.Application; +import xlogo.storage.WSManager; +import xlogo.storage.user.DrawQuality; +import xlogo.storage.user.PenShape; +import xlogo.storage.user.UserConfig; +import xlogo.utils.Utils; +import xlogo.Logo; +import xlogo.kernel.grammar.LogoNumber; +import xlogo.kernel.grammar.LogoType; +import xlogo.kernel.grammar.LogoTypeNull; +import xlogo.kernel.gui.*; +import xlogo.kernel.perspective.*; +/** + * Title : XLogo + * Description : XLogo is an interpreter for the Logo + * programming language + * @author Loïc Le Coq + */ + public class DrawPanel extends JPanel implements MouseMotionListener,MouseListener { + public static final LogoTypeNull nullType=new LogoTypeNull(); + private static final long serialVersionUID = 1L; + public Turtle tortue; + public Turtle[] tortues; + + /** + * When a turtle is active on screen, its number is added to this stack + * @uml.property name="tortues_visibles" + * @uml.associationEnd multiplicity="(0 -1)" elementType="java.lang.String" + */ + public Stack<String> tortues_visibles; + + /** + * this int indicates the window mode, default 0 + */ + protected static int WINDOW_MODE = 0; + /** + * WINDOW MODE: 0 <br> + * Turtles can go out the drawing area + */ + protected static final int WINDOW_CLASSIC=0; + /** + * WRAP MODE: 1 <br> + * Turtles can go out the drawing area and reappear on the other side + */ + protected static final int WINDOW_WRAP=1; + /** + * CLOSE MODE: 2 <br> + * Turtles can't go out the drawing area + */ + protected static final int WINDOW_CLOSE=2; + /** + * Perspective MODE <br> + * The screen is a projection of the 3D universe + */ + protected static final int WINDOW_3D=3; + + /** Boolean for animation mode */ + public static boolean classicMode=true; // true si classique false si animation + /** Animation mode: */ + public final static boolean MODE_ANIMATION=false; + /** Classic mode */ + public final static boolean MODE_CLASSIC=true; + + /** + * default predefined colors + */ + public static final Color[] defaultColors={Color.BLACK,Color.RED,Color.GREEN,Color.YELLOW,Color.BLUE, + Color.MAGENTA,Color.CYAN,Color.WHITE,Color.GRAY,Color.LIGHT_GRAY,new Color(128,0,0),new Color(0,128,0), + new Color(0,0,128),new Color(255,128,0),Color.PINK,new Color(128,0,255),new Color(153,102,0)}; + + + /** + * The id for the drawing font (with primitive label) + * @uml.property name="police_etiquette" + */ + protected int police_etiquette; + /** + * The default drawing area color + * @uml.property name="couleurfond" + */ + private Color couleurfond = Color.white; + private Shape shape=null; + private Line2D line; + private Rectangle2D rec; + private Application cadre; + /** This Image is used for Buffering the drawing*/ + public static BufferedImage dessin; + /** + * Graphics of the BufferedImage dessin + * @uml.property name="g" + */ + private Graphics2D g; + /** The scale for the zoom*/ + public static double zoom=1; + + + /** + * All Gui Objects on the drawing area are stored in the GuiMap gm + * @uml.property name="gm" + * @uml.associationEnd multiplicity="(1 1)" + */ + private GuiMap gm; + /** + * The Stroke for the triangle turtle + * @uml.property name="crayon_triangle" + */ + private BasicStroke crayon_triangle = new BasicStroke(1); + /** + * Tools for 3D Mode + * @uml.property name="w3d" + * @uml.associationEnd + */ + private World3D w3d=null; + /** + * Boolean that indicates if the interpreter is recording polygon in 3D Mode + */ + protected static int record3D=0; + protected final static int record3D_NONE=0; + protected final static int record3D_POLYGON=1; + protected final static int record3D_LINE=2; + protected final static int record3D_POINT=3; + protected final static int record3D_TEXT=4; + + /** + * Boolean that indicates if the interpreter is recording polygon in 2D Mode + */ + private static int record2D=0; + private final static int record2D_NONE=0; + private final static int record2D_POLYGON=1; + private Vector<Point2D.Double> stackTriangle; + + + protected static Element3D poly; + + private double[] coords; + private double oldx; + private double oldy; + private double x1; + private double y1; + private double x2; + private double y2; + // Were used for clipping + // private double nx,ny,vx,vy,factor,length; + // private GeneralPath gp; + // private Arc2D clipArc; + private Arc2D arc; + /** + * Button number when user click on the drawing area + * @uml.property name="bouton_souris" + */ + private int bouton_souris=0; // Numéro du bouton de souris appuyé sur la zone de dessin + /** + * Last coords for last mouse event + * @uml.property name="possouris" + */ + private String possouris="[ 0 0 ] "; // Coordonnées du point du dernier événement souris + + /** + * Notify if a mouse event has occured + * @uml.property name="lissouris" + */ + private boolean lissouris=false; //Indique si un événement souris est intervenu depuis le debut du programme + /** + * The rectangular selection zone + * @uml.property name="selection" + */ + private Rectangle selection; + /** + * Color for the rectangular selection + * @uml.property name="colorSelection" + */ + private Color colorSelection; + /** + * The First clicked point when the rectangular selection is created + * @uml.property name="origine" + */ + Point origine; + public DrawPanel(Application cadre){ + this.gm= cadre.getKernel().getWorkspace().getGuiMap(); + setLayout(null); + this.setPreferredSize(new Dimension( + (int)(WSManager.getUserConfig().getImageWidth()*zoom), + (int)(WSManager.getUserConfig().getImageHeight()*zoom))); + this.cadre=cadre; + addMouseListener(this); + addMouseMotionListener(this); + initGraphics(); + } + /** + * This method is used to draw for primitive "forward" + * @param arg LogoType which represents the number of steps to move + */ + protected LogoType av(LogoType number){ + if (number.isException()) return number; + LogoNumber ln=(LogoNumber)number; + return av(ln.getValue()); + + } + /** + * This method is used to draw for primitive "backward" + * @param arg LogoType which represents the number of steps to move + */ + protected LogoType re(LogoType number){ + if (number.isException()) return number; + LogoNumber ln=(LogoNumber)number; + return av(-ln.getValue()); + + } + /** + * This method is used to draw for primitive "right" + * @param arg LogoType which represents the number of steps to rotate + */ + protected LogoType td(LogoType number){ + if (number.isException()) return number; + LogoNumber ln=(LogoNumber)number; + return td(ln.getValue()); + + } /** + * This method is used to draw for primitive "left" + * @param arg LogoType which represents the number of steps to rotate + */ + protected LogoType tg(LogoType number){ + if (number.isException()) return number; + LogoNumber ln=(LogoNumber)number; + return td(-ln.getValue()); + + } + + /** + * This method is used to draw for primitive "forward" and "backward" + * @param arg Number of steps + */ + protected LogoType av(double arg) { + // Graphics2D g=(Graphics2D)dessin.getGraphics(); + + oldx = tortue.corX; + oldy = tortue.corY; + if (DrawPanel.WINDOW_MODE == DrawPanel.WINDOW_CLASSIC) { //mode fenetre + montrecacheTortue(false); + + tortue.corX = tortue.corX + arg + * Math.cos(tortue.angle); + tortue.corY = tortue.corY - arg + * Math.sin(tortue.angle); + if (tortue.isPenDown()) { + g.setStroke(tortue.stroke); // TODO Marko : fix penerase problem. TODO also for other ops, circle etc + if (tortue.isPenReverse()) { + g.setColor(couleurfond); + g.setXORMode(tortue.couleurcrayon); + } else { + g.setColor(tortue.couleurcrayon); + g.setPaintMode(); + } + if (null==line) line=new Line2D.Double(); + /* if (null==gp) gp=new GeneralPath(); + else gp.reset();*/ + if (oldx < tortue.corX){ + x1=oldx;y1=oldy;x2=tortue.corX;y2=tortue.corY; + } + if (oldx>tortue.corX){ + x2=oldx;y2=oldy;x1=tortue.corX;y1=tortue.corY; + } + else if (oldx==tortue.corX){ + if (oldy<tortue.corY){ + x2=oldx;y2=oldy;x1=tortue.corX;y1=tortue.corY; + } + else{ + x1=oldx;y1=oldy;x2=tortue.corX;y2=tortue.corY; + } + } + + line.setLine(x1,y1,x2,y2); + + /* + // perpendicular vector + nx=y1-y2; + ny=x2-x1; + length=Math.sqrt(nx*nx+ny*ny); + if (length!=0){ + factor=(1+tortue.getPenWidth())/length; + vx=x2-x1; + vy=y2-y1; + gp.moveTo((float)(x1-vx*factor-nx*factor), + (float)(y1-vy*factor-ny*factor)); + gp.lineTo((float)(x1-vx*factor+nx*factor), + (float)(y1-vy*factor+ny*factor)); + gp.lineTo((float)(x2+vx*factor+nx*factor), + (float)(y2+vy*factor+ny*factor)); + gp.lineTo((float)(x2+vx*factor-nx*factor), + (float)(y2+vy*factor-ny*factor)); + gp.lineTo((float)(x1-vx*factor-nx*factor), + (float)(y1-vy*factor-ny*factor)); + } + else{ + float width=tortue.getPenWidth()+0.5f; + gp.moveTo((float)(x1-width), + (float)(y1-width)); + gp.lineTo((float)(x1+width), + (float)(y1-width)); + gp.lineTo((float)(x1+width), + (float)(y1+width)); + gp.lineTo((float)(x1-width), + (float)(y1+width)); + gp.lineTo((float)(x1-width), + (float)(y1-width)); + } + shape=gp;*/ + tryRecord2DMode(tortue.corX,tortue.corY); + g.draw(line); + //if (!tortue.isVisible()) + clip(); + // g.dispose(); + } + montrecacheTortue(true); + } else if (DrawPanel.WINDOW_MODE == DrawPanel.WINDOW_WRAP) { //mode enroule + trace_enroule(arg, oldx, oldy); + } else if (DrawPanel.WINDOW_MODE == DrawPanel.WINDOW_CLOSE) { //mode clos + try { + trace_ferme(oldx, oldy, arg); + } catch (LogoError e) { + } + } + else if (DrawPanel.WINDOW_MODE==DrawPanel.WINDOW_3D){ + montrecacheTortue(false); + tortue.X=tortue.X+arg*tortue.getRotationMatrix()[0][1]; + tortue.Y=tortue.Y+arg*tortue.getRotationMatrix()[1][1]; + tortue.Z=tortue.Z+arg*tortue.getRotationMatrix()[2][1]; + + double tmp[]=new double[3]; + tmp[0]=tortue.X; + tmp[1]=tortue.Y; + tmp[2]=tortue.Z; + + tmp=this.toScreenCoord(tmp,true); + tortue.corX = tmp[0]; + tortue.corY = tmp[1]; + + + if (tortue.isPenDown()) { + if (tortue.isPenReverse()) { + g.setColor(couleurfond); + g.setXORMode(tortue.couleurcrayon); + } else { + g.setColor(tortue.couleurcrayon); + g.setPaintMode(); + } + if (null==line) line=new Line2D.Double(); + + if (oldx < tortue.corX){ + x1=oldx;y1=oldy;x2=tortue.corX;y2=tortue.corY; + } + if (oldx>tortue.corX){ + x2=oldx;y2=oldy;x1=tortue.corX;y1=tortue.corY; + } + else if (oldx==tortue.corX){ + if (oldy<tortue.corY){ + x2=oldx;y2=oldy;x1=tortue.corX;y1=tortue.corY; + } + else{ + x1=oldx;y1=oldy;x2=tortue.corX;y2=tortue.corY; + } + } + + line.setLine(x1,y1,x2,y2); + + g.draw(line); + clip(); + } + montrecacheTortue(true); + } + return DrawPanel.nullType; + } + + /** + * This method is used for drawing with primitive "right" or "left" + * @param arg The angle to rotate + */ + protected LogoType td(double arg) { +// System.out.println(tortue.angle); + if (tortue.isVisible()) + montrecacheTortue(false); + if (!enabled3D()){ + tortue.heading = ((tortue.heading + arg) % 360 + 360) % 360; + tortue.angle = Math.toRadians(90 - tortue.heading); + } + else{ + tortue.setRotationMatrix(w3d.multiply(tortue.getRotationMatrix(),w3d.rotationZ(-arg))); + double[] tmp=w3d.rotationToEuler(tortue.getRotationMatrix()); + tortue.heading=tmp[2]; + tortue.roll=tmp[1]; + tortue.pitch=tmp[0]; + } + if (tortue.isVisible()) + montrecacheTortue(true); + Interprete.operande = false; + + return DrawPanel.nullType; + } + /** + * This method is used for drawing with primitive "rightroll" or "leftroll" + * @param arg + */ + protected void rightroll(double arg) { +// System.out.println(tortue.angle); + if (tortue.isVisible()) + montrecacheTortue(false); + if (enabled3D()){ + tortue.setRotationMatrix(w3d.multiply(tortue.getRotationMatrix(),w3d.rotationY(-arg))); + double[] tmp=w3d.rotationToEuler(tortue.getRotationMatrix()); + tortue.heading=tmp[2]; + tortue.roll=tmp[1]; + tortue.pitch=tmp[0]; + } + if (tortue.isVisible()) + montrecacheTortue(true); + Interprete.operande = false; + } + /** + * This method is used for drawing with primitive "uppitch" or "downpitch" + * @param arg + */ + protected void uppitch(double arg) { +// System.out.println(tortue.angle); + if (tortue.isVisible()) + montrecacheTortue(false); + if (enabled3D()){ + tortue.setRotationMatrix(w3d.multiply(tortue.getRotationMatrix(),w3d.rotationX(arg))); + double[] tmp=w3d.rotationToEuler(tortue.getRotationMatrix()); + tortue.heading=tmp[2]; + tortue.roll=tmp[1]; + tortue.pitch=tmp[0]; + } + if (tortue.isVisible()) + montrecacheTortue(true); + Interprete.operande = false; + } + /** + * This method set the turtle's Roll + * @param arg The new roll + */ + protected void setRoll(double arg){ + if (tortue.isVisible()) + montrecacheTortue(false); + tortue.roll=arg; + tortue.setRotationMatrix(w3d.EulerToRotation(-tortue.roll, tortue.pitch, -tortue.heading)); + if (tortue.isVisible()) + montrecacheTortue(true); + Interprete.operande=false; + } + /** + * This method set the turtle's heading + * @param arg The new heading + */ + protected void setHeading(double arg){ + if (tortue.isVisible()) + montrecacheTortue(false); + tortue.heading=arg; + tortue.setRotationMatrix(w3d.EulerToRotation(-tortue.roll, tortue.pitch, -tortue.heading)); + if (tortue.isVisible()) + montrecacheTortue(true); + Interprete.operande=false; + } + /** + * This method set the turtle's pitch + * @param arg The new pitch + */ + protected void setPitch(double arg){ + if (tortue.isVisible()) + montrecacheTortue(false); + tortue.pitch=arg; + tortue.setRotationMatrix(w3d.EulerToRotation(-tortue.roll, tortue.pitch, -tortue.heading)); + if (tortue.isVisible()) + montrecacheTortue(true); + Interprete.operande=false; + } + /** + * + * This method set the turtle's orientation + * @param arg The new orientation + * @throws LogoError If the list doesn't contain three numbers + */ + protected void setOrientation(String arg) throws LogoError{ + initCoords(); + if (tortue.isVisible()) + montrecacheTortue(false); + extractCoords(arg,Utils.primitiveName("3d.setorientation")); + tortue.roll = coords[0]; + tortue.pitch = coords[1]; + tortue.heading = coords[2]; + tortue.setRotationMatrix(w3d.EulerToRotation(-tortue.roll, tortue.pitch, -tortue.heading)); + if (tortue.isVisible()) + montrecacheTortue(true); + Interprete.operande=false; + } + /** + * Primitive "origine" + */ + protected void origine(){ // primitive origine + try { + if (!enabled3D()) + fpos("0 0"); + else fpos("0 0 0"); + } catch (LogoError e) { + } + if (tortue.isVisible()) + montrecacheTortue(false); + tortue.heading = 0; + tortue.angle = Math.PI / 2; + tortue.roll=0; + tortue.pitch=0; + if (enabled3D()) + tortue.setRotationMatrix(w3d.EulerToRotation(-tortue.roll, tortue.pitch, -tortue.heading)); + if (tortue.isVisible()) + montrecacheTortue(true); + } + + + /** + * Primitive distance + * @param liste The coords + * @param nom + * @return The distance from the turtle position to this point + * @throws LogoError If bad format list + */ + protected double distance(String liste) throws LogoError { + + initCoords(); + extractCoords(liste,Utils.primitiveName("distance")); + double distance; + if (!enabled3D()){ + coords=this.toScreenCoord(coords,false); + distance = Math.sqrt(Math.pow(tortue.corX - coords[0], 2) + + Math.pow(tortue.corY - coords[1], 2)); + } + else distance= Math.sqrt(Math.pow(tortue.X - coords[0], 2) + + Math.pow(tortue.Y - coords[1], 2)+Math.pow(tortue.Z - coords[2], 2)); + return distance; + } + protected double[] vers3D(String liste) throws LogoError{ + double[] tmp=new double [3]; + initCoords(); + extractCoords(liste,Utils.primitiveName("vers")); + tmp[0]=coords[0]-tortue.X; + tmp[1]=coords[1]-tortue.Y; + tmp[2]=coords[2]-tortue.Z; + double length=Math.sqrt(Math.pow(tmp[0],2)+Math.pow(tmp[1],2)+Math.pow(tmp[2],2)); + if (length==0) return tmp; + tmp[0]=tmp[0]/length; + tmp[1]=tmp[1]/length; + tmp[2]=tmp[2]/length; + double heading=Math.acos(tmp[1]); + double f=Math.sin(heading); + double tr_x=-tmp[0]/f; + double tr_y=-tmp[2]/f; + double roll=Math.atan2(tr_y, tr_x); + tmp[0]=-Math.toDegrees(roll); + tmp[1]=0; + tmp[2]=-Math.toDegrees(heading); + for (int i=0;i<3;i++){ + if (tmp[i]<0) tmp[i]+=360; + } + return tmp; + } + + /** + * Primitive towards in 2D MODE + * @param liste the coordinate for the point + * @return the rotation angle + * @throws LogoError if Bad format List + */ + protected double vers2D(String liste) throws LogoError{ + initCoords(); + extractCoords(liste,Utils.primitiveName("vers")); + double angle; + coords=this.toScreenCoord(coords, false); + if (tortue.corY == coords[1]) { + if (coords[0] > tortue.corX) + angle = 90; + else if (coords[0] == tortue.corX) + angle = 0; + else + angle = 270; + } + else if (tortue.corX == coords[0]) { + if (tortue.corY > coords[1]) + angle = 0; + else + angle = 180; + } + else { + angle = Math.toDegrees(Math.atan(Math + .abs(coords[0] - tortue.corX) + / Math.abs(tortue.corY - coords[1]))); + // System.out.println(coords[0] - tortue.corX+" "+Math.abs(tortue.corY - coords[1])+" "+angle); + if (coords[0] > tortue.corX && coords[1] > tortue.corY) + angle = 180 - angle; // 2eme quadrant + else if (coords[0] < tortue.corX && coords[1] > tortue.corY) + angle = 180 + angle; // 3eme quadrant + else if (coords[0] < tortue.corX && coords[1] < tortue.corY) + angle = 360 - angle; // 4eme quadrant + } + return angle; + } + /** + * Draw with the primitive "setposition" in 2D mode or 3D + * @param liste The list with the coordinates to move + * @throws LogoError If the coordinates are invalid + */ + protected void fpos(String liste) throws LogoError { + initCoords(); + oldx = tortue.corX; + oldy = tortue.corY; + extractCoords(liste,Utils.primitiveName("drawing.fpos")); + montrecacheTortue(false); + if (enabled3D()) { + tortue.X = coords[0]; + tortue.Y = coords[1]; + tortue.Z = coords[2]; + } + coords=toScreenCoord(coords,true); + + tortue.corX=coords[0]; + tortue.corY=coords[1]; + if (tortue.isPenDown()) { + if (tortue.isPenReverse()) { + g.setColor(couleurfond); + g.setXORMode(tortue.couleurcrayon); + } else { + g.setColor(tortue.couleurcrayon); + g.setPaintMode(); + } + if (null==line) line=new Line2D.Double(); + if (oldx < tortue.corX){ + x1=oldx;y1=oldy;x2=tortue.corX;y2=tortue.corY; + } + if (oldx>tortue.corX){ + x2=oldx;y2=oldy;x1=tortue.corX;y1=tortue.corY; + } + else if (oldx==tortue.corX){ + if (oldy<tortue.corY){ + x2=oldx;y2=oldy;x1=tortue.corX;y1=tortue.corY; + } + else{ + x1=oldx;y1=oldy;x2=tortue.corX;y2=tortue.corY; + } + } + line.setLine(x1,y1,x2,y2); + tryRecord2DMode(tortue.corX,tortue.corY); + g.draw(line); + clip(); + } + montrecacheTortue(true); + } + public void drawEllipseArc(double xAxis,double yAxis, double angleRotation,double xCenter,double yCenter, double angleStart, double angleExtent){ + montrecacheTortue(false); + arc=new Arc2D.Double(-xAxis,-yAxis,2*xAxis,2*yAxis,angleStart,angleExtent,Arc2D.Double.OPEN); + if (tortue.isPenReverse()) { + g.setColor(couleurfond); + g.setXORMode(tortue.couleurcrayon); + } else { + g.setColor(tortue.couleurcrayon); + g.setPaintMode(); + } + double tmpx=WSManager.getUserConfig().getImageWidth()/2+xCenter; + double tmpy=WSManager.getUserConfig().getImageHeight()/2-yCenter; + g.translate(tmpx, tmpy); + g.rotate(-angleRotation); + g.draw(arc); + g.rotate(angleRotation); + g.translate(-tmpx, -tmpy); + /* if (null==clipArc) clipArc=new Arc2D.Double(); + clipArc.setArcByCenter(tortue.corX,tortue.corY, + rayon+2+tortue.getPenWidth(),0,360, Arc2D.OPEN);*/ + clip(); + montrecacheTortue(true); // on efface la tortue si elle st visible + } + /** + * This method draw an arc on the drawing area + * @param rayon The radius + * @param pangle Starting angle + * @param fangle End angle + * @throws LogoError + */ + protected void arc(double rayon, double pangle, double fangle) throws LogoError { + // Put fangle and pangle between 0 and 360 + fangle = ((90 - fangle) % 360); + pangle = ((90 - pangle) % 360); + if (fangle<0) fangle+=360; + if (pangle<0) pangle+=360; + // Calculate angle extend + double angle=pangle-fangle; + if (angle<0) angle+=360; + montrecacheTortue(false); + if (null==arc) arc=new Arc2D.Double(); + if (!enabled3D()){ + if (DrawPanel.WINDOW_MODE==DrawPanel.WINDOW_WRAP) centers=new Vector<Point2D.Double>(); + arc2D(tortue.corX,tortue.corY,rayon,fangle,angle); + + /* if (null==gp) gp=new GeneralPath(); + else gp.reset(); + gp.moveTo((float)(tortue.corX-rayon-tortue.getPenWidth()), + (float)(tortue.corY-rayon-tortue.getPenWidth()); + gp.lineTo((float)(tortue.corX-rayon-tortue.getPenWidth()), + (float)(tortue.corY-rayon-tortue.getPenWidth())); + gp.lineTo((float)(tortue.corX-rayon-tortue.getPenWidth()), + (float)(tortue.corY-rayon-tortue.getPenWidth())); + gp.lineTo((float)(tortue.corX-rayon-tortue.getPenWidth()), + (float)(tortue.corY-rayon-tortue.getPenWidth())); + gp.lineTo((float)(tortue.corX-rayon-tortue.getPenWidth()), + (float)(tortue.corY-rayon-tortue.getPenWidth()));*/ +/* if (null==rec) rec=new Rectangle2D.Double(); + rec.setRect(tortue.corX-rayon-tortue.getPenWidth(), + tortue.corY-rayon-tortue.getPenWidth(), + 2*(rayon+tortue.getPenWidth()),2*(rayon+tortue.getPenWidth()));*/ + clip(); + + } + else{ + arcCircle3D(rayon,fangle,angle); + } + montrecacheTortue(true); + } + private void arc2D(double x, double y, double radius,double fangle, double angle){ + arc.setArcByCenter(x,y,radius, + fangle,angle, Arc2D.OPEN); + if (tortue.isPenReverse()) { + g.setColor(couleurfond); + g.setXORMode(tortue.couleurcrayon); + } else { + g.setColor(tortue.couleurcrayon); + g.setPaintMode(); + } + g.draw(arc); + clip(); + + UserConfig uc = WSManager.getUserConfig(); + int w = uc.getImageWidth(); + int h = uc.getImageHeight(); + + if (DrawPanel.WINDOW_MODE==DrawPanel.WINDOW_WRAP){ + if (x+radius>w&& x<=w){ + pt=new Point2D.Double(-w+x,y); + if (! centers.contains(pt)) { + centers.add(pt); + arc2D(-w+x,y,radius,fangle,angle); + } + } + if (x-radius<0&& x>=0){ + pt=new Point2D.Double(w+x,y); + if (! centers.contains(pt)) { + centers.add(pt); + arc2D(w+x,y,radius,fangle,angle); + } + } + if (y-radius<0&& y>=0){ + pt=new Point2D.Double(x,h+y); + if (! centers.contains(pt)) { + centers.add(pt); + arc2D(x,h+y,radius,fangle,angle); + } + } + if (y+radius>h&&y<=h){ + pt=new Point2D.Double(x,-h+y); + if (! centers.contains(pt)) { + centers.add(pt); + arc2D(x,-h+y,radius,fangle,angle); + } + } + } + } + + + private void arcCircle3D(double radius,double angleStart,double angleExtent) throws LogoError{ + if (null==arc) arc=new Arc2D.Double(); + arc.setArcByCenter(0,0,radius, + angleStart,angleExtent, Arc2D.OPEN); + Shape s=transformShape(arc); + if (tortue.isPenReverse()) { + g.setColor(couleurfond); + g.setXORMode(tortue.couleurcrayon); + } else { + g.setColor(tortue.couleurcrayon); + g.setPaintMode(); + } + g.draw(s); + if (DrawPanel.record3D==DrawPanel.record3D_LINE||DrawPanel.record3D==DrawPanel.record3D_POLYGON){ + recordArcCircle3D(radius,angleStart,angleExtent); + } + } + + + /** + * + * returns the color for the pixel "ob" + * @param liste: The list containing the coordinates of the pixel + * @return Color of this pixel + * @throws LogoError If the list doesn't contain coordinates + */ + protected Color guessColorPoint(String liste) throws LogoError { + UserConfig uc = WSManager.getUserConfig(); + int w = uc.getImageWidth(); + int h = uc.getImageHeight(); + initCoords(); + extractCoords(liste,Utils.primitiveName("tc")); + coords=toScreenCoord(coords,false); + int couleur = -1; + int x=(int)coords[0]; + int y=(int)coords[1]; + if (0 < x && x < w && 0 < y && y < h) { + couleur = DrawPanel.dessin.getRGB(x, y); + } + return new Color(couleur); + } + /** + * This method draw a circle from the turtle position on the drawing area + * @param radius The radius of the circle + * @throws LogoError + */ + protected void circle(double radius) throws LogoError { + montrecacheTortue(false); + if (null==arc) arc=new Arc2D.Double(); + if (!enabled3D()){ + if (DrawPanel.WINDOW_MODE==DrawPanel.WINDOW_WRAP) centers=new Vector<Point2D.Double>(); + circle2D(tortue.corX,tortue.corY,radius); + /* if (null==clipArc) clipArc=new Arc2D.Double(); + clipArc.setArcByCenter(tortue.corX,tortue.corY, + rayon+2+tortue.getPenWidth(),0,360, Arc2D.OPEN);*/ + } + else{ + circle3D(radius); + } + montrecacheTortue(true); // on efface la tortue si elle st visible + } + /** + * This method draws a circle in 2D mode in WRAP mode, makes recursion to draw all circle part on the screen + * @param x x circle center + * @param y y circle center + * @param circle radius + * @uml.property name="pt" + */ + private Point2D.Double pt; + /** + * @uml.property name="centers" + */ + private Vector <Point2D.Double> centers; + private void circle2D(double x,double y, double radius){ + UserConfig uc = WSManager.getUserConfig(); + int w = uc.getImageWidth(); + int h = uc.getImageHeight(); + + arc.setArcByCenter(x,y,radius, + 0,360, Arc2D.OPEN); + + if (tortue.isPenReverse()) { + g.setColor(couleurfond); + g.setXORMode(tortue.couleurcrayon); + } else { + g.setColor(tortue.couleurcrayon); + g.setPaintMode(); + } + g.draw(arc); + clip(); + if (DrawPanel.WINDOW_MODE==DrawPanel.WINDOW_WRAP){ + if (x+radius>w&& x<=w){ + pt=new Point2D.Double(-w+x,y); + if (! centers.contains(pt)) { + centers.add(pt); + circle2D(-w+x,y,radius); + } + } + if (x-radius<0&& x>=0){ + pt=new Point2D.Double(w+x,y); + if (! centers.contains(pt)) { + centers.add(pt); + circle2D(w+x,y,radius); + } + } + if (y-radius<0&& y>=0){ + pt=new Point2D.Double(x,h+y); + if (! centers.contains(pt)) { + centers.add(pt); + circle2D(x,h+y,radius); + } + } + if (y+radius>h&&y<=h){ + pt=new Point2D.Double(x,-h+y); + if (! centers.contains(pt)) { + centers.add(pt); + circle2D(x,-h+y,radius); + } + } + } + } + + /** + * used for drawing with primitive "dot" + * @param liste The list with the dot coordinates + * @throws LogoError If the list is invalid coordinates + */ + protected void point(String liste) throws LogoError { + UserConfig uc = WSManager.getUserConfig(); + int w = uc.getImageWidth(); + int h = uc.getImageHeight(); + initCoords(); + extractCoords(liste,Utils.primitiveName("drawing.point")); + coords=toScreenCoord(coords,true); +// System.out.println(coords[0]+" "+coords[1]+" "+h+" "+w); + if (coords[0]>0 && coords[1]>0 && coords[0]<w && coords[1] < h) { + if (tortue.isPenReverse()) { + g.setColor(couleurfond); + g.setXORMode(tortue.couleurcrayon); + + } else { + g.setColor(tortue.couleurcrayon); + g.setPaintMode(); + } + if (rec==null) rec=new Rectangle2D.Double(); + // High quality + if (uc.getQuality()==DrawQuality.HIGH){ + double width=tortue.getPenWidth(); + rec.setRect(coords[0]-width+0.5,coords[1]-width+0.5, + 2*width,2*width); + } + // Normal or Low Quality + else{ + // penWidth is 2k or 2k+1?? + int intWidth=(int)(2*tortue.getPenWidth()+0.5); + if (intWidth%2==1){ + double width=tortue.getPenWidth()-0.5; +// System.out.println(coords[0]+" "+coords[1]); + rec.setRect(coords[0]-width,coords[1]-width, + 2*width+1,2*width+1); + } + else { + double width=tortue.getPenWidth(); + rec.setRect(coords[0]-width,coords[1]-width, + 2*width,2*width); + } + } + if (uc.getPenShape()==PenShape.SQUARE){ // MAKE ENUM + g.fill(rec); + } + else if (uc.getPenShape()==PenShape.OVAL){ + if (null==arc) arc=new Arc2D.Double(); + arc.setArcByCenter(coords[0],coords[1],0,0,360,Arc2D.OPEN); + g.draw(arc); + } + clip(); + } + } + + + + /** + * @throws LogoError + * + */ + private void circle3D(double radius) throws LogoError{ + + // In camera world, + // the circle is the intersection of + // - a plane with the following equation: ax+by+cz+d=0 <-> f(x,y,z)=0 + // - and a sphere with the following equation: (x-tx)^2+(y-ty)^2+(z-tz)^2=R^2 <-> g(x,y,z)=0 + // I found the cone equation resolving f(x/lambda,y/lambda,z/lambda)=0=g(x/lambda,y/lambda,z/lambda) + + double[] v=new double[3]; + for(int i=0;i<3;i++){ + v[i]=tortue.getRotationMatrix()[i][2]; + } + v[0]+=w3d.xCamera; + v[1]+=w3d.yCamera; + v[2]+=w3d.zCamera; + w3d.toCameraWorld(v); + // Now v contains coordinates of a normal vector to the plane in camera world coordinates + double a=v[0]; + double b=v[1]; + double c=v[2]; + + // We convert the turtle coordinates + v[0]=tortue.X; + v[1]=tortue.Y; + v[2]=tortue.Z; + w3d.toCameraWorld(v); + + double x=v[0]; + double y=v[1]; + double z=v[2]; + // We calculate the number d for the plane equation + double d=-a*x-b*y-c*z; + + // We have to work with Bigdecimal because of precision problems + + BigDecimal[] big=new BigDecimal[6]; + BigDecimal bx=new BigDecimal(x); + BigDecimal by=new BigDecimal(y); + BigDecimal bz=new BigDecimal(z); + BigDecimal ba=new BigDecimal(a); + BigDecimal bb=new BigDecimal(b); + BigDecimal bc=new BigDecimal(c); + BigDecimal bd=new BigDecimal(d); + BigDecimal deux=new BigDecimal("2"); + BigDecimal screenDistance=new BigDecimal(w3d.screenDistance); + BigDecimal bradius=new BigDecimal(String.valueOf(radius)); + + // Now we calculate the coefficient for the conic ax^2+bxy+cy^2+dx+ey+f=0 + // Saved in an array + + // lambda=(x*x+y*y+z*z-radius*radius); + BigDecimal lambda=bx.pow(2).add(by.pow(2)).add(bz.pow(2)).subtract(bradius.pow(2)); + + // x^2 coeff + // d*d+2*d*x*a+a*a*lambda; + big[0]=bd.pow(2).add(bd.multiply(bx).multiply(ba).multiply(deux)).add(ba.pow(2).multiply(lambda)); + // xy coeff + // 2*d*x*b+2*d*y*a+2*a*b*lambda; + big[1]=deux.multiply(bd).multiply(bx).multiply(bb).add(deux.multiply(bd).multiply(by).multiply(ba)).add(deux.multiply(ba).multiply(bb).multiply(lambda)); + // y^2 coeff + // d*d+2*d*y*b+b*b*lambda; + big[2]=bd.pow(2).add(bd.multiply(by).multiply(bb).multiply(deux)).add(bb.pow(2).multiply(lambda)); + // x coeff + // 2*w3d.screenDistance*(d*x*c+d*z*a+lambda*a*c); + big[3]=deux.multiply(screenDistance).multiply(bd.multiply(bx).multiply(bc).add(bd.multiply(bz).multiply(ba)).add(lambda.multiply(ba).multiply(bc))); + // y coeff + // 2*w3d.screenDistance*(d*y*c+d*z*b+lambda*b*c); + big[4]=deux.multiply(screenDistance).multiply(bd.multiply(by).multiply(bc).add(bd.multiply(bz).multiply(bb)).add(lambda.multiply(bb).multiply(bc))); + // Numbers + // Math.pow(w3d.screenDistance,2)*(d*d+2*d*z*c+lambda*c*c); + big[5]=screenDistance.pow(2).multiply(bd.pow(2).add(deux.multiply(bd).multiply(bz).multiply(bc)).add(lambda.multiply(bc.pow(2)))); + new Conic(this,big); + if (DrawPanel.record3D==DrawPanel.record3D_LINE||DrawPanel.record3D==DrawPanel.record3D_POLYGON){ + recordArcCircle3D(radius,0,360); + } + } + /** + * This method records this circle in the polygon's List + * @param radius The circle's radius + * @param angleStart The starting Angle + * @param angleExtent The angle for the sector + * @throws LogoError + */ + public void recordArcCircle3D(double radius,double angleStart,double angleExtent) throws LogoError{ + double[][] d=tortue.getRotationMatrix(); + Matrix3d m=new Matrix3d(d[0][0],d[0][1],d[0][2],d[1][0],d[1][1],d[1][2],d[2][0],d[2][1],d[2][2]); + // Vector X + Point3d v1=new Point3d(radius/1000,0,0); + Transform3D t=new Transform3D(m,new Vector3d(),1); + t.transform(v1); + // Vector Y + Point3d v2=new Point3d(0,radius/1000,0); + t.transform(v2); + + // Turtle position + Point3d pos=new Point3d(tortue.X/1000,tortue.Y/1000,tortue.Z/1000); + int indexMax=(int)angleExtent; + if (indexMax!=angleExtent) indexMax+=2; + else indexMax+=1; + if (null!=DrawPanel.poly&&DrawPanel.poly.getVertexCount()>1) + DrawPanel.poly.addToScene(); + if (DrawPanel.record3D==DrawPanel.record3D_POLYGON) { + DrawPanel.poly=new ElementPolygon(cadre.getViewer3D()); + DrawPanel.poly.addVertex(pos, tortue.couleurcrayon); + } + else { + DrawPanel.poly=new ElementLine(cadre.getViewer3D(), cadre.getKernel().getActiveTurtle().getPenWidth()); + } + + for(int i=0;i<indexMax-1;i++){ + Point3d tmp1=new Point3d(v1); + tmp1.scale(Math.cos(Math.toRadians(angleStart+i))); + Point3d tmp2=new Point3d(v2); + tmp2.scale(Math.sin(Math.toRadians(angleStart+i))); + tmp1.add(tmp2); + tmp1.add(pos); + DrawPanel.poly.addVertex(tmp1, tortue.couleurcrayon); + } + Point3d tmp1=new Point3d(v1); + tmp1.scale(Math.cos(Math.toRadians(angleStart+angleExtent))); + Point3d tmp2=new Point3d(v2); + tmp2.scale(Math.sin(Math.toRadians(angleStart+angleExtent))); + tmp1.add(tmp2); + tmp1.add(pos); + DrawPanel.poly.addVertex(tmp1, tortue.couleurcrayon); + } + +/** + * Load an image and draw it on the drawing area + * @param image The image to draw + */ + protected void chargeimage(BufferedImage image) { + if (tortue.isVisible()) + montrecacheTortue(false); + g.setPaintMode(); + g.translate(tortue.corX, tortue.corY); + g.rotate(-tortue.angle); + g.drawImage(image, null, 0,0); + g.rotate(tortue.angle); + g.translate(-tortue.corX, -tortue.corY); + + clip(); +// repaint(); +/* if (null==rec) rec=new Rectangle2D.Double(); + rec.setRect(tortue.corX,tortue.corY, + image.getWidth(),image.getHeight());*/ + if (tortue.isVisible()) + montrecacheTortue(true); + } + /** + * To guess the length before going out the drawing area in WRAP mode + * @param mini The minimum distance before leaving + * @param maxi The maximum distance before leaving + * @param oldx The X turtle location + * @param oldy The Y turtle location + * @return the number of steps (Recursive dichotomy) + */ + private double trouve_longueur(double mini, double maxi, double oldx, double oldy) { + UserConfig uc = WSManager.getUserConfig(); + int w = uc.getImageWidth(); + int h = uc.getImageHeight(); + // renvoie la longueur dont on peut encore avancer + if (Math.abs(maxi - mini) < 0.5){ + return (mini);} + else { + double milieu = (mini + maxi) / 2; + double nx = oldx + milieu * Math.cos(tortue.angle); + double ny = oldy - milieu * Math.sin(tortue.angle); + if (nx < 0 || nx > w|| ny < 0 || ny > h) + return trouve_longueur(mini, milieu, oldx, oldy); + else + return trouve_longueur(milieu, maxi, oldx, oldy); + } + } +/** + * This method is used for drawing with primitive forward, backward in WRAP MODE + * @param arg the length to forward + * @param oldx X position + * @param oldy Y position + */ + private void trace_enroule(double arg, double oldx, double oldy) { + UserConfig uc = WSManager.getUserConfig(); + int w = uc.getImageWidth(); + int h = uc.getImageHeight(); + + boolean re = false; + if (arg < 0) { + re = true; + } + double diagonale=Math.sqrt(Math.pow(w,2)+Math.pow(h,2))+1; + double longueur; + if (re) + longueur = trouve_longueur(0, -diagonale, oldx, oldy); + else + longueur = trouve_longueur(0, diagonale, oldx, oldy); +// System.out.println(diagonale+" "+oldx+" "+oldy); + while (Math.abs(longueur) < Math.abs(arg)) { + // System.out.println(Math.abs(longueur)+" "+Math.abs(arg)); + arg -= longueur; + DrawPanel.WINDOW_MODE = DrawPanel.WINDOW_CLASSIC; + av(longueur); + //System.out.println(Math.abs(longueur)+" "+Math.abs(arg)); + if (cadre.error) + break; //permet d'interrompre avec le bouton stop + DrawPanel.WINDOW_MODE = DrawPanel.WINDOW_WRAP; + if (uc.getTurtleSpeed() != 0) { + try { + Thread.sleep(uc.getTurtleSpeed() * 5); + } catch (InterruptedException e) { + } + } + if (tortue.isVisible()) + this.montrecacheTortue(false); + if (re) tortue.heading=(tortue.heading+180)%360; + if (tortue.corX > w-1 + && (tortue.heading < 180 && tortue.heading != 0)) { + tortue.corX = 0; + if (tortue.corY > h-1 + && (tortue.heading > 90 && tortue.heading < 270)) + tortue.corY = 0; + else if (tortue.corY < 1 + && (tortue.heading < 90 || tortue.heading > 270)) + tortue.corY = h; + } else if (tortue.corX < 1 && tortue.heading > 180) { + tortue.corX = w; + if (tortue.corY > h-1 + && (tortue.heading > 90 && tortue.heading < 270)) + tortue.corY = 0; + else if (tortue.corY < 1 + && (tortue.heading < 90 || tortue.heading > 270)) + tortue.corY = h; + } else if (tortue.corY > h-1) + tortue.corY = 0; + else if (tortue.corY < 1) + tortue.corY = h; + if (re) tortue.heading=(tortue.heading+180)%360; + if (tortue.isVisible()) + this.montrecacheTortue(true); + if (re) + longueur = trouve_longueur(0, -diagonale, tortue.corX, + tortue.corY); + else + longueur = trouve_longueur(0, diagonale, tortue.corX, + tortue.corY); + } + DrawPanel.WINDOW_MODE = DrawPanel.WINDOW_CLASSIC; + if (!cadre.error) + av(arg); + DrawPanel.WINDOW_MODE = DrawPanel.WINDOW_WRAP; + } +/** + * This method is used for drawing with primitive forward, backward in CLOSE MODE + * @param oldx X position + * @param oldy Y position + * @param arg The length to forward + * @throws LogoError + */ + private void trace_ferme(double oldx, double oldy, double arg) throws LogoError { + UserConfig uc = WSManager.getUserConfig(); + int w = uc.getImageWidth(); + int h = uc.getImageHeight(); + + boolean re = false; + double longueur; + double diagonale=Math.sqrt(Math.pow(w,2)+Math.pow(h,2))+1; + if (arg < 0) + re = true; + if (re) + longueur = trouve_longueur(0, -diagonale, oldx, oldy); + else + longueur = trouve_longueur(0, diagonale, oldx, oldy); + if (Math.abs(longueur) < Math.abs(arg)) + throw new LogoError(Logo.messages + .getString("erreur_sortie1") + + "\n" + + Logo.messages.getString("erreur_sortie2") + + Math.abs((int) (longueur))); + else { + DrawPanel.WINDOW_MODE = DrawPanel.WINDOW_CLASSIC; + av(arg); + DrawPanel.WINDOW_MODE = DrawPanel.WINDOW_CLOSE; + } + } + /** + * This method extract coords from a list <br> + * X is stored in coords(0], Y stored in coords[1], Z Stored in coords[2] + * @param liste The list + * @param prim The calling primitive + * @throws LogoError If List isn't a list coordinate + */ + + private void extractCoords(String liste,String prim)throws LogoError{ + StringTokenizer st = new StringTokenizer(liste); + try { + for(int i=0;i<coords.length;i++){ + coords[i]=1; + if (!st.hasMoreTokens()) + throw new LogoError(prim + + " " + Logo.messages.getString("n_aime_pas") + liste + + Logo.messages.getString("comme_parametre")); + String element = st.nextToken(); + if (element.equals("-")) { + if (st.hasMoreTokens()) + element = st.nextToken(); + coords[i] = -1; + } + coords[i] = coords[i] * Double.parseDouble(element); + } + + } catch (NumberFormatException e) { + throw new LogoError(prim + + " " + Logo.messages.getString("n_aime_pas") + liste + + Logo.messages.getString("comme_parametre")); + } + if (st.hasMoreTokens()) + throw new LogoError(prim + + " " + Logo.messages.getString("n_aime_pas") + liste + + Logo.messages.getString("comme_parametre")); + } + /** + * This method sets the drawing area to perspective mode + */ + + protected void perspective(){ + UserConfig uc = WSManager.getUserConfig(); + if (!enabled3D()) { + uc.setDrawXAxis(false); + uc.setDrawYAxis(false); + uc.setDrawGrid(false); + change_image_tortue(cadre,"tortue0.png"); + montrecacheTortue(false); + DrawPanel.WINDOW_MODE=DrawPanel.WINDOW_3D; + w3d=new World3D(); + montrecacheTortue(true); + } + } + /** + * This method sets the drawing area to Wrap, Close or Window mode + * @param id The window Mode + */ + protected void setWindowMode(int id){ + if (DrawPanel.WINDOW_MODE!=id) { + montrecacheTortue(false); + DrawPanel.WINDOW_MODE=id; + w3d=null; + montrecacheTortue(true); + } + } + + + /** + * This method converts the coordinates contained in "coords" towards the coords on the drawing area + */ + double[] toScreenCoord(double[] coord,boolean drawPoly){ + // If Mode perspective is active + if (enabled3D()){ + // w3d.toScreenCoord(coord); + // camera world + // If we have to record the polygon coordinates + if (DrawPanel.record3D!=DrawPanel.record3D_NONE&&DrawPanel.record3D!=DrawPanel.record3D_TEXT&&drawPoly){ + + DrawPanel.poly.addVertex(new Point3d(coord[0]/1000,coord[1]/1000,coord[2]/1000),tortue.couleurcrayon); + } + + w3d.toCameraWorld(coord); + + // Convert to screen Coordinates + w3d.cameraToScreen(coord); + } + // Mode2D + else { + UserConfig uc = WSManager.getUserConfig(); + int w = uc.getImageWidth(); + int h = uc.getImageHeight(); + coord[0]=w/2+coord[0]; + coord[1]=h/2-coord[1]; + } + return coord; + } + + + + /** + * This method creates an instance of coord with the valid size:<br> + * size 2 for 2D coordinates<br> + * size 3 for 3D coordinates + */ + + private void initCoords(){ + + if (null==coords) coords=new double[2]; + if (enabled3D()){ + if (coords.length!=3) coords=new double[3]; + } + else { + if (coords.length!=2) coords=new double[2]; + } + } + public boolean enabled3D(){ + return (DrawPanel.WINDOW_MODE==DrawPanel.WINDOW_3D); + } + + /** + * For hideturtle and showturtle + */ + protected void ct_mt() { + if (null == tortue.tort) { + g.setXORMode(couleurfond); + g.setColor(tortue.couleurcrayon); + tortue.drawTriangle(); + BasicStroke crayon_actuel = (BasicStroke) g.getStroke(); + if (crayon_actuel.getLineWidth() == 1) + g.draw(tortue.triangle); + else { + g.setStroke(crayon_triangle); + g.draw(tortue.triangle); + g.setStroke(crayon_actuel); + } + } else { + g.setXORMode(couleurfond); + double angle = Math.PI / 2 - tortue.angle; + float x = (float) (tortue.corX * Math.cos(angle) + tortue.corY + * Math.sin(angle)); + float y = (float) (-tortue.corX * Math.sin(angle) + tortue.corY + * Math.cos(angle)); + g.rotate(angle); + g.drawImage(tortue.tort, (int) x - tortue.largeur / 2, + (int) y - tortue.hauteur / 2, this); + g.rotate(-angle); + } +/* if (null==rec) rec=new Rectangle2D.Double(); + rec.setRect(tortue.corX - tortue.gabarit, + tortue.corY - tortue.gabarit, + tortue.gabarit * 2, + tortue.gabarit * 2); + */ + clip(); + +/* clip((int) (tortue.corX - tortue.gabarit), + (int) (tortue.corY - tortue.gabarit), + tortue.gabarit * 2, tortue.gabarit * 2);*/ + } + /** + * When the turtle has to be redrawn, this method erase the turtle on the drawing screen + * + */ + protected void montrecacheTortue(boolean b) { + g.setColor(couleurfond); + for (int i = 0; i < tortues_visibles.size(); i++) { + int id = Integer.parseInt(tortues_visibles.get(i)); + // Turtle triangle + if (null == tortues[id].tort) { + g.setXORMode(couleurfond); + g.setColor(tortues[id].couleurmodedessin); + tortues[id].drawTriangle(); + BasicStroke crayon_actuel = (BasicStroke) g.getStroke(); + if (crayon_actuel.getLineWidth() == 1) + g.draw(tortues[id].triangle); + else { + g.setStroke(crayon_triangle); + g.draw(tortues[id].triangle); + g.setStroke(crayon_actuel); + } + } else { + // Image turtle + g.setXORMode(couleurfond); + double angle = Math.PI / 2 - tortues[id].angle; + float x = (float) (tortues[id].corX * Math.cos(angle) + tortues[id].corY + * Math.sin(angle)); + float y = (float) (-tortues[id].corX * Math.sin(angle) + tortues[id].corY + * Math.cos(angle)); + g.rotate(angle); + g.drawImage(tortues[id].tort, (int) x + - tortues[id].largeur / 2, (int) y + - tortues[id].hauteur / 2, this); + g.rotate(-angle); + } + /*if (null==rec) rec=new Rectangle2D.Double(); + rec.setRect(tortues[id].corX - tortues[id].gabarit, + tortues[id].corY - tortues[id].gabarit, + tortues[id].gabarit * 2, + tortues[id].gabarit * 2); + shape=rec;*/ + if (b) clip(); + } + } + + + +/* private void montrecacheTortue() { + // Graphics2D g=(Graphics2D)dessin.getGraphics(); + g.setColor(couleurfond); + for (int i = 0; i < tortues_visibles.size(); i++) { + int id = Integer.parseInt(String.valueOf(tortues_visibles + .get(i))); + + if (null == tortues[id].tort) { + g.setXORMode(couleurfond); + g.setColor(tortues[id].couleurmodedessin); + tortues[id].coord(); + BasicStroke crayon_actuel = (BasicStroke) g.getStroke(); + if (crayon_actuel.getLineWidth() == 1) + g.draw(tortues[id].triangle); + else { + g.setStroke(crayon_triangle); + g.draw(tortues[id].triangle); + g.setStroke(crayon_actuel); + } + } else { + g.setXORMode(couleurfond); + double angle = Math.PI / 2 - tortues[id].angle; + float x = (float) (tortues[id].corX * Math.cos(angle) + tortues[id].corY + * Math.sin(angle)); + float y = (float) (-tortues[id].corX * Math.sin(angle) + tortues[id].corY + * Math.cos(angle)); + g.rotate(angle); + g.drawImage(tortues[id].tort, (int) x + - tortues[id].largeur / 2, (int) y + - tortues[id].hauteur / 2, cadre.getArdoise()); + g.rotate(-angle); + } + /* if (null==rec) rec=new Rectangle2D.Double(); + rec.setRect(tortues[id].corX - tortues[id].gabarit, + tortues[id].corY - tortues[id].gabarit, + tortues[id].gabarit * 2, + tortues[id].gabarit * 2); + + clip(); + // g.dispose(); + } + } +*/ + /** + * Primitive clearscreen + */ + protected void videecran() { + // Delete all Gui Component + Set<String> set=gm.keySet(); + Iterator<String> it=set.iterator(); + while(it.hasNext()){ + String element=it.next(); + gui=gm.get(element).getGuiObject(); + it.remove(); + if (SwingUtilities.isEventDispatchThread()){ + remove(gui); + validate(); + } + else { + try{ + SwingUtilities.invokeAndWait(new Runnable(){ + public void run(){ + remove(gui); + validate(); + } + }); + } + catch(Exception e){} + } + } + + + // Delete List Polygon in 3D mode +// DrawPanel.listPoly=new Vector<Shape3D>(); +// DrawPanel.listText=new Vector<TransformGroup>(); + // Erase the 3d viewer if visible + if (null!=cadre.getViewer3D()) { + cadre.getViewer3D().clearScreen(); + System.gc(); + } + + UserConfig uc = WSManager.getUserConfig(); + int w = uc.getImageWidth(); + int h = uc.getImageHeight(); + + g.setPaintMode(); + couleurfond=uc.getScreencolor(); + g.setColor(uc.getScreencolor()); + g.fillRect(0, 0, w,h); + stopRecord2DPolygon(); + + // Draw Grid + g.setStroke(new BasicStroke(1)); + drawGrid(); + drawXAxis(); + drawYAxis(); + // Init Turtles + if (null == tortues[0]) + tortues[0] = new Turtle(cadre); + // The active turtle will be the turtle 0 + tortue = tortues[0]; + tortue.id = 0; + // We delete all other turtles + for (int i = 1; i < tortues.length; i++) { + tortues[i] = null; + } + tortues_visibles.removeAllElements(); + tortues_visibles.push("0"); + g.setColor(tortue.couleurcrayon); + clip(); + tortue.init(); + tortue.setVisible(true); + g.setStroke(new BasicStroke(1)); + montrecacheTortue(true); + // Update the selection frame + updateColorSelection(); + + } + /** + * Primitive wash + */ + protected void nettoie() { + UserConfig uc = WSManager.getUserConfig(); + int w = uc.getImageWidth(); + int h = uc.getImageHeight(); + + stopRecord2DPolygon(); + g.setPaintMode(); + g.setColor(couleurfond); + g.fillRect(0, 0, w,h); + + drawGrid(); + /* Réinitialiser les tortues + if (null == tortues[0]) + tortues[0] = new Tortue(cadre); + tortue = tortues[0]; //la tortue active sera à présent la + // numéro 0 + tortue.id = 0; + for (int i = 1; i < tortues.length; i++) { //On élimine les + // autres tortues + tortues[i] = null; + } + tortues_visibles.removeAllElements(); + tortues_visibles.push("0");*/ + g.setColor(tortue.couleurcrayon); + clip(); + + if (tortue.isVisible()) + montrecacheTortue(true); + else + tortues_visibles=new Stack<String>(); + } + /** + * Used for primitive fillzone + * @param x + * @param y + * @param increment + * @param couleur_frontiere + * @return + */ + + private int bornes_remplis_zone(int x, int y, int increment, int couleur_frontiere) { + UserConfig uc = WSManager.getUserConfig(); + int w = uc.getImageWidth(); +// System.out.println(x+" "+y); + while (!meme_couleur(DrawPanel.dessin.getRGB(x, y) ,couleur_frontiere)) { + DrawPanel.dessin.setRGB(x, y, couleur_frontiere); + x = x + increment; + if (!(x > 0 && x < w-1)) + break; + } + return x - increment; + } + /** + * Are the two color equals? + * @param col1 The first color + * @param col2 The second color + * @return true or false + */ + private boolean meme_couleur(int col1,int col2){ +/* if (Config.quality==Logo.QUALITY_HIGH){ + int rouge1 = (col1 >> 16) & 0xFF; + int vert1 = (col1 >> 8) & 0xFF; + int bleu1 = col1 & 0xFF; + int rouge2 = (col2 >> 16) & 0xFF; + int vert2 = (col2 >> 8) & 0xFF; + int bleu2 = col2 & 0xFF; + int tolerance=120; + int diff_rouge=rouge1-rouge2; + int diff_bleu=bleu1-bleu2; + int diff_vert=vert1-vert2; + boolean rouge;boolean vert; boolean bleu; + if (rouge1>rouge2){ + if (rouge1-rouge2< 128 -rouge2/2) rouge=true; + else rouge=false; + } + else{ + if (rouge2-rouge1<rouge2/2) rouge=true; + else rouge=false; + } + if (vert1>vert2){ + if (vert1-vert2< 128 -vert2/2) vert=true; + else vert=false; + } + else{ + if (vert2-vert1<vert2/2) vert=true; + else vert=false; + } + if (bleu1>bleu2){ + if (bleu1-bleu2< 128 -bleu2/2) bleu=true; + else bleu=false; + } + else{ + if (bleu2-bleu1<bleu2/2) bleu=true; + else bleu=false; + } + + return rouge&&bleu&| +// if (Math.abs(rouge1-rouge2)<tolerance&&Math.abs(vert1-vert2)<tolerance&&Math.abs(bleu1-bleu2)<tolerance&&Math.abs(rouge1+bleu1+vert1-rouge2-bleu2-vert2)<450) +// return true; + // else return false; + } + else{*/ + return (col1==col2); + //} + } + /** + * Primitive fillzone + */ + protected void rempliszone() { + montrecacheTortue(false); + int x = (int) (tortue.corX + 0.5); + int y = (int) (tortue.corY + 0.5); + UserConfig uc = WSManager.getUserConfig(); + int w = uc.getImageWidth(); + int h = uc.getImageHeight(); + if (x > 0 & x < w & y > 0 & y < h) { + int couleur_origine = DrawPanel.dessin.getRGB(x, y); + int couleur_frontiere = tortue.couleurcrayon.getRGB(); + // System.out.println(couleur_origine+" " +couleur_frontiere); + Stack<Point> pile_germes = new Stack<Point>(); + boolean couleurs_differentes = !meme_couleur(couleur_origine,couleur_frontiere); + if (couleurs_differentes) + pile_germes.push(new Point(x, y)); + while (!pile_germes.isEmpty()) { + + Point p = pile_germes.pop(); + int xgerme = p.x; + int ygerme = p.y; + int xmax = bornes_remplis_zone(xgerme, ygerme, 1, + couleur_frontiere); + int xmin=0; + if (xgerme>0) xmin = bornes_remplis_zone(xgerme - 1, ygerme, -1, + couleur_frontiere); + boolean ligne_dessus = false; + boolean ligne_dessous = false; + for (int i = xmin; i < xmax + 1; i++) { + //on recherche les germes au dessus et au dessous + if (ygerme > 0 + && meme_couleur(DrawPanel.dessin.getRGB(i, ygerme - 1) ,couleur_frontiere)) { + if (ligne_dessus) + pile_germes.push(new Point(i - 1, ygerme - 1)); + ligne_dessus = false; + } else { + ligne_dessus = true; + if (i == xmax && ygerme > 0) + pile_germes.push(new Point(xmax, ygerme - 1)); + } + if (ygerme < h-1 + && meme_couleur(DrawPanel.dessin.getRGB(i, ygerme + 1),couleur_frontiere)) { + if (ligne_dessous) + pile_germes.push(new Point(i - 1, ygerme + 1)); + ligne_dessous = false; + } else { + ligne_dessous = true; + if (i == xmax && ygerme < h-1) + pile_germes.push(new Point(xmax, ygerme + 1)); + } + } + } + clip(); + montrecacheTortue(true); + } + } + /** + * Used for primitive "fill" + * @param x + * @param y + * @param increment + * @param couleur_crayon + * @param couleur_origine + * @return + */ + private int bornes_remplis(int x, int y, int increment, int couleur_crayon, + int couleur_origine) { + UserConfig uc = WSManager.getUserConfig(); + int w = uc.getImageWidth(); + while (DrawPanel.dessin.getRGB(x, y) == couleur_origine) { + DrawPanel.dessin.setRGB(x, y, couleur_crayon); + x = x + increment; + if (!(x > 0 && x < w-1)) + break; + } + return x - increment; + } + /** + * Primitive "fill" + */ + protected void remplis() { + montrecacheTortue(false); + int x = (int) (tortue.corX + 0.5); + int y = (int) (tortue.corY + 0.5); + UserConfig uc = WSManager.getUserConfig(); + int w = uc.getImageWidth(); + int h = uc.getImageHeight(); + + if (x > 0 & x < w & y > 0 & y < h) { + int couleur_origine = DrawPanel.dessin.getRGB(x, y); + int couleur_crayon = tortue.couleurcrayon.getRGB(); + if (x > 0 & x < w & y > 0 & y < h) { + Stack<Point> pile_germes = new Stack<Point>(); + boolean couleurs_differentes = !(couleur_origine == couleur_crayon); + if (couleurs_differentes) + pile_germes.push(new Point(x, y)); + while (!pile_germes.isEmpty()) { + Point p = pile_germes.pop(); + int xgerme = p.x; + int ygerme = p.y; + // System.out.println(xgerme+" "+ygerme); + int xmax = bornes_remplis(xgerme, ygerme, 1, couleur_crayon, + couleur_origine); + int xmin=0; + if (xgerme>0) xmin = bornes_remplis(xgerme - 1, ygerme, -1, + couleur_crayon, couleur_origine); + // System.out.println("xmax "+xmax+"xmin "+xmin); + boolean ligne_dessus = false; + boolean ligne_dessous = false; + for (int i = xmin; i < xmax + 1; i++) { + //on recherche les germes au dessus et au dessous + if (ygerme > 0 + && DrawPanel.dessin.getRGB(i, ygerme - 1) != couleur_origine) { + if (ligne_dessus) + pile_germes.push(new Point(i - 1, ygerme - 1)); + ligne_dessus = false; + } else { + ligne_dessus = true; + if (i == xmax && ygerme > 0) + pile_germes.push(new Point(xmax, ygerme - 1)); + } + if (ygerme < h-1 + && DrawPanel.dessin.getRGB(i, ygerme + 1) != couleur_origine) { + if (ligne_dessous) + pile_germes.push(new Point(i - 1, ygerme + 1)); + ligne_dessous = false; + } else { + ligne_dessous = true; + if (i == xmax && ygerme < h-1) + pile_germes.push(new Point(xmax, ygerme + 1)); + } + } + } + clip(); + montrecacheTortue(true); + } + } + } + /** + * Primitive "label" + * @param mot The word to write on the drawing area + */ + protected void etiquette(String mot) { + // Graphics2D g = (Graphics2D) Ardoise.dessin.getGraphics(); + montrecacheTortue(false); + if (!enabled3D()){ + double angle = Math.PI / 2 - tortue.angle; + if(DrawPanel.WINDOW_MODE==DrawPanel.WINDOW_WRAP) centers=new Vector<Point2D.Double>(); + etiquette2D(tortue.corX,tortue.corY,angle,mot); +/* g.rotate(angle); + g.setPaintMode(); + g.setColor(tortue.couleurcrayon); + float x = (float) (tortue.corX * Math.cos(angle) + tortue.corY + * Math.sin(angle)); + float y = (float) (-tortue.corX * Math.sin(angle) + tortue.corY + * Math.cos(angle)); + g.setFont(Panel_Font.fontes[police_etiquette] + .deriveFont((float) tortue.police)); + g.drawString(mot, x, y); + g.rotate(-angle);*/ + } + else{ + FontRenderContext frc=g.getFontRenderContext(); + GlyphVector gv=g.getFont().createGlyphVector(frc, mot); + Shape outline=gv.getOutline(0, 0); + Shape s=transformShape(outline); + g.setPaintMode(); + g.setColor(tortue.couleurcrayon); + g.fill(s); + if (record3D==DrawPanel.record3D_TEXT){ + Text2D text=new Text2D( + mot,new Color3f(tortue.couleurcrayon), UserConfig.fontes[police_etiquette].getName(), + tortue.police,Font.PLAIN); + + text.setRectangleScaleFactor(0.001f); + Appearance appear=text.getAppearance(); + PolygonAttributes pa=new PolygonAttributes(); + pa.setCullFace(PolygonAttributes.CULL_NONE); + pa.setBackFaceNormalFlip(true); + appear.setPolygonAttributes(pa); + text.setAppearance(appear); +// if (null==DrawPanel.listText) DrawPanel.listText=new Vector<TransformGroup>(); + TransformGroup tg=new TransformGroup(); + double[][] d=tortue.getRotationMatrix(); + Matrix3d m=new Matrix3d(d[0][0],d[0][1],d[0][2],d[1][0],d[1][1],d[1][2],d[2][0],d[2][1],d[2][2]); + Transform3D t=new Transform3D(m,new Vector3d(tortue.X/1000,tortue.Y/1000,tortue.Z/1000),1); + tg.setTransform(t); + tg.addChild(text); + cadre.getViewer3D().add2DText(tg); +// DrawPanel.listText.add(tg); + } + + + } + montrecacheTortue(true); + if (classicMode) repaint(); + } + private void etiquette2D(double x,double y, double angle, String word){ + if (word.length()==0) return; + + g.setPaintMode(); + g.setColor(tortue.couleurcrayon); + Font f= UserConfig.fontes[police_etiquette] + .deriveFont((float) tortue.police); + g.setFont(f); + g.translate(x, y); + g.rotate(angle); + FontRenderContext frc = g.getFontRenderContext(); + TextLayout layout = new TextLayout(word, f, frc); + Rectangle2D bounds = layout.getBounds(); + float height=(float)bounds.getHeight(); + float width=(float)bounds.getWidth(); + float x1=0,y1=0; + switch(tortue.getLabelHorizontalAlignment()){ + case Turtle.LABEL_HORIZONTAL_ALIGNMENT_LEFT: + x1=0; + break; + case Turtle.LABEL_HORIZONTAL_ALIGNMENT_CENTER: + x1=-width/2; + break; + case Turtle.LABEL_HORIZONTAL_ALIGNMENT_RIGHT: + x1=-width; + break; + } + switch(tortue.getLabelVerticalAlignment()){ + case Turtle.LABEL_VERTICAL_ALIGNMENT_BOTTOM: + y1=0; + break; + case Turtle.LABEL_VERTICAL_ALIGNMENT_CENTER: + y1=height/2; + break; + case Turtle.LABEL_VERTICAL_ALIGNMENT_TOP: + y1=height; + break; + } + layout.draw(g, x1, y1); + g.drawString(word, x1, y1); + g.rotate(-angle); + g.translate(-x, -y); + if (DrawPanel.WINDOW_MODE==DrawPanel.WINDOW_WRAP){ + Rectangle2D.Double rec=new Rectangle2D.Double(0,0,width,height); + AffineTransform at=new AffineTransform(); + at.translate(x, y); + at.rotate(angle); + Rectangle2D bound =at.createTransformedShape(rec).getBounds2D(); + double right= bound.getX()+bound.getWidth()-x; + double left= x-bound.getX(); + double up=y-bound.getY(); + double down=bound.getY()+bound.getHeight()-y; + + UserConfig uc = WSManager.getUserConfig(); + int w = uc.getImageWidth(); + int h = uc.getImageHeight(); + + if (x+right>w&& x<=w){ + pt=new Point2D.Double(-w+x,y); + if (! centers.contains(pt)) { + centers.add(pt); + etiquette2D(-w+x,y,angle,word); + } + } + if (x-left<0&& x>=0){ + pt=new Point2D.Double(w+x,y); + if (! centers.contains(pt)) { + centers.add(pt); + etiquette2D(w+x,y,angle,word); + } + } + if (y-up<0&& y>=0){ + pt=new Point2D.Double(x,h+y); + if (! centers.contains(pt)) { + centers.add(pt); + etiquette2D(x,h+y,angle,word); + } + } + if (y+down>h&&y<=h){ + pt=new Point2D.Double(x,-h+y); + if (! centers.contains(pt)) { + centers.add(pt); + etiquette2D(x,-h+y,angle,word); + } + } + } + } + + /** + * This method transform a plane 2D shape in the shape corresponding to the turtle plane + * @param s the first shape + * @return the new shape after transformation + */ + private Shape transformShape(Shape s){ + PathIterator it=s.getPathIterator(null); + double[] d=new double[6]; + double[][] coor=new double[3][1]; + GeneralPath gp=new GeneralPath(); + double[] end=new double[3]; + double[] ctl1=new double[3]; + double[] ctl2=new double[3]; + boolean b=false; + while(!it.isDone()){ + it.next(); + int id=0; + if (!it.isDone()) id=it.currentSegment(d); + else break; + coor[0][0]=d[0]; + coor[1][0]=-d[1]; + coor[2][0]=0; + coor=w3d.multiply(tortue.getRotationMatrix(), coor); + + end[0]=coor[0][0]+tortue.X; + end[1]=coor[1][0]+tortue.Y; + end[2]=coor[2][0]+tortue.Z; + w3d.toScreenCoord(end); + + if (id==PathIterator.SEG_MOVETO) + gp.moveTo((float)end[0], (float)end[1]); + else if (id==PathIterator.SEG_LINETO) + { + if (!b) { + b=true; + gp.moveTo((float)end[0], (float)end[1]); + } + else gp.lineTo((float)end[0], (float)end[1]); + } + else if (id==PathIterator.SEG_CLOSE){ + gp.closePath(); + } + else { + if (!b) { + b=true; + Point2D p=null; + if (s instanceof Arc2D.Double) + p=((Arc2D.Double)s).getStartPoint(); + else if (s instanceof GeneralPath) + p=((GeneralPath)s).getCurrentPoint(); + coor[0][0]=p.getX(); + coor[1][0]=-p.getY(); + coor[2][0]=0; + coor=w3d.multiply(tortue.getRotationMatrix(), coor); + ctl1[0]=coor[0][0]+tortue.X; + ctl1[1]=coor[1][0]+tortue.Y; + ctl1[2]=coor[2][0]+tortue.Z; + w3d.toScreenCoord(ctl1); + gp.moveTo((float)ctl1[0], (float)ctl1[1]); + } + coor[0][0]=d[2]; + coor[1][0]=-d[3]; + coor[2][0]=0; + coor=w3d.multiply(tortue.getRotationMatrix(), coor); + ctl1[0]=coor[0][0]+tortue.X; + ctl1[1]=coor[1][0]+tortue.Y; + ctl1[2]=coor[2][0]+tortue.Z; + w3d.toScreenCoord(ctl1); + if(id==PathIterator.SEG_QUADTO){ + QuadCurve2D qc=new QuadCurve2D.Double(gp.getCurrentPoint().getX(),gp.getCurrentPoint().getY() + ,end[0], end[1],ctl1[0], ctl1[1]); + gp.append(qc, true);} + else if (id==PathIterator.SEG_CUBICTO){ + coor[0][0]=d[4]; + coor[1][0]=-d[5]; + coor[2][0]=0; + coor=w3d.multiply(tortue.getRotationMatrix(), coor); + + ctl2[0]=coor[0][0]+tortue.X; + ctl2[1]=coor[1][0]+tortue.Y; + ctl2[2]=coor[2][0]+tortue.Z; + + w3d.toScreenCoord(ctl2); + CubicCurve2D qc=new CubicCurve2D.Double(gp.getCurrentPoint().getX(),gp.getCurrentPoint().getY() + ,end[0], end[1],ctl1[0], ctl1[1],ctl2[0], ctl2[1]); + gp.append(qc, true); + } + } + } + return gp; + } + public World3D getWorld3D(){ + return w3d; + } + /** + * primitive setscreencolor + * @param color The Color of the nackground screen + */ + protected void fcfg(Color color) { + couleurfond=color; + updateColorSelection(); + if (enabled3D()){ + if (cadre.getViewer3D()!=null){ + cadre.getViewer3D().updateBackGround(couleurfond); + } + } + nettoie(); + } + /** + * Primitive setpencolor + * @param color The pen Color + */ + protected void fcc(Color color) { + if (tortue.isVisible()&&null==tortue.tort) montrecacheTortue(false); + tortue.couleurcrayon = color; + tortue.couleurmodedessin = color; + if (tortue.isVisible()&&null==tortue.tort) montrecacheTortue(true); + } + + /** + * Primitive "guiposition" + * @param id The id for the gui Object + * @param liste The Coordinates list + * @param name The translated name for the primitive "guiposition" + * @throws LogoError If coordinates list is invalid + */ + protected void guiposition(String id, String liste,String name) throws LogoError{ + if (guiExist(id)){ + initCoords(); + extractCoords(liste,name); + coords=toScreenCoord(coords,false); + gm.get(id).setLocation((int)coords[0],(int)coords[1]); + } + } + /** + * Draw the Gui object refered with "id" + * @param id The Gui Object Id + * @throws LogoError If this object doesn't exist + */ + protected void guiDraw(String id) throws LogoError{ + if (guiExist(id)){ + GuiComponent gc=gm.get(id); + add(gc.getGuiObject()); + validate(); + repaint(); + // updateGuiComponents(); + } + } + /** + * @uml.property name="gui" + * @uml.associationEnd + */ + private javax.swing.JComponent gui; + /** + * This method erases a Gui on the drawing area + * @param id The Gui Object id + * @throws LogoError + */ + + protected void guiRemove(String id) throws LogoError{ + if (guiExist(id)){ + gui=gm.get(id).getGuiObject(); + gm.remove(id); + if (SwingUtilities.isEventDispatchThread()){ + remove(gui); + validate(); + } + else { + try{ + SwingUtilities.invokeAndWait(new Runnable(){ + public void run(){ + remove(gui); + validate(); + } + }); + } + catch(Exception e){} + } + repaint(); + } + } + private StringBuffer extractList(String list) throws LogoError{ + StringBuffer sb=new StringBuffer(); + int compteur=0; + int i=0; + while(list.length()!=0){ + char c=list.charAt(i); + if (c=='[') compteur++; + else if (c==']') { + if (compteur==0) return sb; + else compteur--; + } + sb.append(c); + i++; + } + throw new LogoError("[ "+list+" "+Logo.messages.getString("pas_liste")); + } + + protected void guiAction(String id, String liste) throws LogoError{ + if (guiExist(id)){ + GuiComponent gc=gm.get(id); + // If gc is a JButton + if (gc.isButton()){ + ((GuiButton)gc).setAction(Utils.decoupe(liste)); + if (!gc.hasAction()){ + ((javax.swing.JButton)gc.getGuiObject()).addActionListener(gc); + gc.hasAction=true; + } + } + // gc is a JcomboBox + else if (gc.isMenu()){ + liste=liste.trim(); + int i=0; + while(liste.length()!=0){ + if (liste.charAt(0)=='['){ + liste=liste.substring(1).trim(); + StringBuffer sb=extractList(liste); + liste=liste.substring(sb.length()+1).trim(); + ((GuiMenu)gc).setAction(sb, i); + i++; + } + else throw new LogoError(liste.charAt(0)+" "+Logo.messages.getString("pas_liste")); + } + GuiMenu gm=(GuiMenu)gc; + if (!gm.hasAction){ + gm.hasAction=true; + ((javax.swing.JComboBox)gc.getGuiObject()).addActionListener(gm); + } + } + } + } + private boolean guiExist(String id) throws LogoError{ + if (gm.containsKey(id.toLowerCase())) return true; + else throw new LogoError(Logo.messages.getString("no_gui")+" "+id); + } +// boolean access=false; + private void clip(){ + if (classicMode){ + + //access=true; +// refresh(); + + repaint(); + /* if (SwingUtilities.isEventDispatchThread()){ + repaint(); + } + else { + try { + + SwingUtilities.invokeLater(new Runnable(){ + public void run(){ + repaint(); + } + }); + } + catch(Exception e2){} + }*/ + } + /*Rectangle rec1=cadre.jScrollPane1.getViewport().getViewRect(); + boolean inter=sh.intersects(rec1); + if (inter){ + if (classicMode){ + repaint(); + } + }*/ + } + public void setQuality(DrawQuality q){ + /* + * Marko Zivkovic + * I improved all the qualities for one level. I introduced a super high quality that is assigned to "high" + * and dropped the low quality, which is now the old normal/default quality + */ + + if (q==DrawQuality.HIGH){ + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON); + g.setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_QUALITY); + g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,RenderingHints.VALUE_STROKE_PURE); + } + else if(q==DrawQuality.LOW){ + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_DEFAULT); + g.setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_DEFAULT); + g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT); + } + else { //normal + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON); + g.setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_QUALITY); + g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + } + } + public void change_image_tortue(Application cadre, String chemin) { + if (tortue.isVisible()) + montrecacheTortue(false); + if (chemin.equals("tortue0.png")) { + tortue.tort = null; + tortue.largeur = 26; + tortue.hauteur = 26; + } else { + //ON teste tout d'abord si le chemin est valide + if (null == Utils.class.getResource(chemin)) + chemin = "tortue1.png"; + tortue.tort = Toolkit.getDefaultToolkit().getImage( + Utils.class.getResource(chemin)); + MediaTracker tracker = new MediaTracker(cadre.getFrame()); + tracker.addImage(tortue.tort, 0); + try { + tracker.waitForID(0); + } catch (InterruptedException e1) { + } + double largeur_ecran = Toolkit.getDefaultToolkit().getScreenSize() + .getWidth(); + // On fait attention à la résolution de l'utilisateur + double facteur = largeur_ecran / 1024.0; + + if ((int) (facteur + 0.001) != 1) { + tortue.largeur = tortue.tort.getWidth(cadre.getFrame()); + tortue.hauteur = tortue.tort.getHeight(cadre.getFrame()); + tortue.tort = tortue.tort.getScaledInstance( + (int) (facteur * tortue.largeur), + (int) (facteur * tortue.hauteur), + Image.SCALE_SMOOTH); + tracker = new MediaTracker(cadre.getFrame()); + tracker.addImage(tortue.tort, 0); + try { + tracker.waitForID(0); + } catch (InterruptedException e1) { + } + } + tortue.largeur = tortue.tort.getWidth(cadre.getFrame()); + tortue.hauteur = tortue.tort.getHeight(cadre.getFrame()); + } + tortue.gabarit = Math.max(tortue.hauteur, + tortue.largeur); + if (tortue.isVisible()) + montrecacheTortue(true); + + } + // animation + protected void setAnimation(boolean predic){ + if (predic==classicMode){ + if (predic) { + cadre.getHistoryPanel().active_animation(); + } + else { + cadre.getHistoryPanel().stop_animation(); + repaint(); + } + } + } + + protected void setGraphicsFont(Font f){ + g.setFont(f); + } + protected Font getGraphicsFont(){ + return g.getFont(); + } + protected void setStroke(Stroke st){ + g.setStroke(st); + } + public Color getBackgroundColor(){ + return couleurfond; + } + protected void setBackgroundColor(Color c){ + couleurfond=c; + } + protected void updateColorSelection(){ + float r=(255-couleurfond.getRed())/255; + float v=(255-couleurfond.getGreen())/255; + float b=(255-couleurfond.getBlue())/255; + colorSelection=new Color(r,v,b,0.2f); + } + public void setNumberOfTurtles(int max){ + WSManager.getUserConfig().setMaxTurtles(max); + Turtle[] tampon = tortues.clone(); + tortues = new Turtle[max]; + int borne_sup=Math.min(tampon.length,tortues.length); + for(int i=0;i<borne_sup;i++){ + tortues[i]=tampon[i]; + } + for(int i=tortues_visibles.size()-1;i>-1;i--){ + int integer=Integer.parseInt(tortues_visibles.get(i)); + if (integer>=max){ + tortues_visibles.remove(i); + } + } + } + protected void initGraphics(){ + UserConfig uc = WSManager.getUserConfig(); + int w = uc.getImageWidth(); + int h = uc.getImageHeight(); + + police_etiquette=UserConfig.police_id(WSManager.getWorkspaceConfig().getFont()); + // init all turtles + tortues = new Turtle[uc.getMaxTurtles()]; + tortues_visibles=new Stack<String>(); + tortue=new Turtle(cadre); + tortues[0] = tortue; + tortue.id = 0; + tortues_visibles.push("0"); + for (int i = 1; i < tortues.length; i++) { + // All other turtles are null + tortues[i] = null; + } + g=(Graphics2D)dessin.getGraphics(); + couleurfond=uc.getScreencolor(); + setQuality(uc.getQuality()); + g.setColor(uc.getScreencolor()); + g.fillRect(0,0,w,h); + g.setColor(uc.getScreencolor()); + if (!enabled3D()){ + drawGrid(); + drawXAxis(); + drawYAxis(); + } + MediaTracker tracker; + if (0==uc.getActiveTurtle()) { + g.setXORMode(couleurfond); + tortue.drawTriangle(); + g.setColor(tortue.couleurcrayon); + g.draw(tortue.triangle); + } + else { + g.setXORMode(couleurfond); + tracker=new MediaTracker(cadre.getFrame()); + tracker.addImage(tortue.tort,0); + try{tracker.waitForID(0);} + catch(InterruptedException e){} + if (tracker.checkID(0)) g.drawImage(tortue.tort, w/2 - tortue.largeur / 2, + h/2 - tortue.hauteur/2, this); + } + updateColorSelection(); + } + + private void resizeAllGuiComponents(double d){ + // Resize all GuiComponent + Set<String> set=gm.keySet(); + Iterator<String> it=set.iterator(); + while (it.hasNext()){ + String element=it.next(); + GuiComponent gui=gm.get(element); + gui.getGuiObject().setSize((int)(gui.getOriginalWidth()*d), + (int)(gui.getOriginalHeight()*d) ); + Font f=gui.getGuiObject().getFont(); + gui.getGuiObject().setFont(f.deriveFont((float)(WSManager.getWorkspaceConfig().getFont().getSize()*d))); + double x=gui.getLocation().x/zoom; + double y=gui.getLocation().y/zoom; + gui.setLocation((int)(x*d),(int)(y*d)); + + } + + } + + + /** + * Make a zoom on the drawing area + * @param d The absolute factor + */ + public void zoom(double d, boolean zoomIn){ + // Disable zoom buttons + //cadre.setZoomEnabled(false); // TODO REMOVE ZOOM COMPLETELY? + + javax.swing.JViewport jv=cadre.scrollArea.getViewport(); + Point p=jv.getViewPosition(); + Rectangle r=jv.getVisibleRect(); + + + // If a selection rectangle is displaying on the drawing area + // And If zoomout has been pressed + // Zooming on the rectangular selection + if (null!=selection&&cadre.commande_isEditable()&&zoomIn){ + int originalWidth=jv.getWidth(); + double width=selection.getWidth(); + d=zoom*originalWidth/width; + p=selection.getLocation(); + r.width=selection.width; + // adjust height in the same ratio as width + r.height=r.height*(int)width/originalWidth; + // erase selection + selection=null; + } + // Resize all Gui Components on the drawing area + resizeAllGuiComponents(d); + + double oldZoom=zoom; + zoom=d; + + /* + * ------------------------------------- + * | | + * | ------------------------- | + * | | | | + * | | | | + * | | x-- dx----- | --> CenterView Point of the rectangle + * | | | | | + * | | dy | | + * | ------------------------- | + * ------------------------------------- + * */ + + UserConfig uc = WSManager.getUserConfig(); + int w = uc.getImageWidth(); + int h = uc.getImageHeight(); + + double dx=Math.min(r.width,w*oldZoom)/2; + double dy=Math.min(r.height,h*oldZoom)/2; + Point centerView=new Point((int)(p.x+dx),(int)(p.y+dy)); + + // Dynamically modify the drawing Area size + setPreferredSize(new java.awt.Dimension( + (int)(w*zoom) + ,(int)(h*zoom))); + + SwingUtilities.invokeLater(new PositionJViewport(jv, + new Point((int)(centerView.x/oldZoom*zoom-dx), + (int)(centerView.y/oldZoom*zoom-dy)))); + + } + private Color getTransparencyColor(int color,int trans){ + Color c=new Color(color); + return new Color(c.getRed(),c.getGreen(),c.getBlue(),trans); + } + /** + * Draw the horizontal axis + */ + private void drawXAxis(){ + UserConfig uc = WSManager.getUserConfig(); + int w = uc.getImageWidth(); + int h = uc.getImageHeight(); + + if (uc.isDrawXAxis()){ + g.setColor(getTransparencyColor(uc.getAxisColor(),128)); + g.drawLine(0,h/2,w,h/2); + for (int i=w/2%uc.getXAxis();i<w;i=i+uc.getXAxis()){ + g.drawLine(i, h/2-2, i, h/2+2); + g.setFont(new Font("Dialog",Font.PLAIN,10)); + String tick=String.valueOf(i-w/2); + FontMetrics fm=g.getFontMetrics(); + int back=fm.stringWidth(String.valueOf(tick))/2; + // if the both axes are drawn, the zero has to translated + // So we don't draw the zero + if (i!=w/2||!uc.isDrawYAxis()) g.drawString(tick, i-back, h/2+20); + } + } + } + /** + * Draw the vertical axis + */ + private void drawYAxis(){ + UserConfig uc = WSManager.getUserConfig(); + int w = uc.getImageWidth(); + int h = uc.getImageHeight(); + + if (uc.isDrawYAxis()){ + g.setColor(getTransparencyColor(uc.getAxisColor(),128)); + g.drawLine(w/2,0,w/2,h); + for (int i=h/2%uc.getYAxis();i<h;i=i+uc.getYAxis()){ + g.drawLine( w/2-2, i, w/2+2,i); + g.setFont(new Font("Dialog",Font.PLAIN,10)); + String tick=String.valueOf(h/2-i); + // If both axes are drawn, zero is translated + if (i==h/2&&uc.isDrawXAxis()) g.drawString("0", w/2+10, i-5); + else g.drawString(tick, w/2+10, i+5); + } + } + } + private void drawGrid(){ + UserConfig uc = WSManager.getUserConfig(); + int w = uc.getImageWidth(); + int h = uc.getImageHeight(); + + if (uc.isDrawGrid()){ + g.setStroke(new BasicStroke(1)); + g.setColor(getTransparencyColor(uc.getGridColor(),100)); + for (int i=w/2%uc.getXGrid();i<w;i=i+uc.getXGrid()) + g.drawLine(i, 0, i, h); + + for (int i=h/2%uc.getYGrid();i<h;i=i+uc.getYGrid()) + g.drawLine(0,i, w, i); + } + } + // In animation mode, we have to wait for the drawing to be finished before modifying graphics. + // Thread must be synchronized. + protected synchronized void refresh(){ + repaint(); + try{ + wait(); + } + catch(InterruptedException e){} + + } + + protected synchronized void paintComponent(Graphics graph){ + super.paintComponent(graph); + Graphics2D g2d=(Graphics2D)graph; + if (null==shape){ + g2d.setClip(cadre.scrollArea.getViewport().getViewRect()); + } + else { + g2d.setClip(shape); + shape=null; + } + g2d.scale(DrawPanel.zoom,DrawPanel.zoom); + g2d.drawImage(dessin,0,0,this); + g2d.scale(1/DrawPanel.zoom,1/DrawPanel.zoom); + if (!Affichage.execution_lancee&&null!=selection&&cadre.commande_isEditable()){ + g2d.setColor(colorSelection); + g2d.fillRect(selection.x, selection.y, selection.width, selection.height); + } + notify(); + } + public void active_souris(){ + lissouris=false; + } + public boolean get_lissouris(){ + return lissouris; + } + public int get_bouton_souris(){ + lissouris=false; + return bouton_souris; + } + public String get_possouris(){ + lissouris=false; + return possouris; + } + public void mousePressed(MouseEvent e){ + if (!Affichage.execution_lancee) { + selection=new Rectangle(); + origine=new Point(e.getPoint()); + selection.setSize(0, 0); + } + } + public void mouseReleased(MouseEvent e){} + public void mouseClicked(MouseEvent ev){ + UserConfig uc = WSManager.getUserConfig(); + int w = uc.getImageWidth(); + int h = uc.getImageHeight(); + if (!Affichage.execution_lancee){ + selection=null; + origine=null; + repaint(); + } + else{ + lissouris=true; + bouton_souris=ev.getButton(); + Point point=ev.getPoint(); + possouris="[ "+(point.x-w/2)+" "+(h/2-point.y)+" ] "; + } + } + + public void mouseExited(MouseEvent e){ + } + public void mouseEntered(MouseEvent e){ + } + // Select an export area + public void mouseDragged(MouseEvent e){ + if (!Affichage.execution_lancee&&null!=selection){ + // First, we test if we need to move the scrollbars + Point pos=e.getPoint(); + javax.swing.JViewport jv=cadre.scrollArea.getViewport(); + Point viewPosition=jv.getViewPosition(); + Rectangle r=jv.getVisibleRect(); + r.setLocation(viewPosition); + // Is the point visible on screen? + boolean b=r.contains(pos); + + UserConfig uc = WSManager.getUserConfig(); + int w = uc.getImageWidth(); + int h = uc.getImageHeight(); + + // Move the scroolPane if necessary + if (!b){ + int x,y; + if (pos.x<viewPosition.x) x=Math.max(0,pos.x); + else if (pos.x>viewPosition.x+r.width) x=Math.min(pos.x-r.width,(int)(w*zoom-r.width)); + else x=viewPosition.x; + if (pos.y<viewPosition.y) y=Math.max(0,pos.y); + else if (pos.y>viewPosition.y+r.height) y=Math.min(pos.y-r.height,(int)(h*zoom-r.height)); + else y=viewPosition.y; + jv.setViewPosition(new Point(x,y)); + } + + // Then , drawing the selection area + + selection.setFrameFromDiagonal(origine, e.getPoint()); + repaint(); + } + } + + public void mouseMoved(MouseEvent ev) { + UserConfig uc = WSManager.getUserConfig(); + int w = uc.getImageWidth(); + int h = uc.getImageHeight(); + + lissouris = true; + bouton_souris = 0; + Point point = ev.getPoint(); + possouris = "[ " + (point.x - w / 2) + " " + (h / 2 - point.y) + " ] "; + } + + protected void addToGuiMap(GuiComponent gc) throws xlogo.kernel.LogoError { + gm.put(gc); + } + + // This method modifies all Shape for any turtle on screen + protected void updateAllTurtleShape() { + for (int i = 0; i < tortues.length; i++) { + if (null != tortues[i]) { + tortues[i].fixe_taille_crayon(2 * tortues[i].getPenWidth()); + } + } + } + /** + * Saves the a part of the drawing area as an image + * @param name The image name + * @param coords The upper left corner and the right bottom corner + */ + protected void saveImage(String name, int[] coords){ + BufferedImage buffer=getImagePart(coords); + String lowerName=name.toLowerCase(); + String format="png"; + if (lowerName.endsWith(".jpg")||lowerName.endsWith(".jpeg")) { + format="jpg"; + } + else if (!lowerName.endsWith(".png")) { + name=name+".png"; + } + name=WSManager.getUserConfig().getDefaultFolder()+File.separator+name; + try{ + File f=new File(name); + ImageIO.write(buffer, format, f); + } + catch(IOException e){} + + } + /** + * Return a part of the drawing area as an image + * @return + */ + private BufferedImage getImagePart(int[] coords){ + Image pic=DrawPanel.dessin; + if (zoom!=1){ + pic=createImage(new FilteredImageSource(pic.getSource(), + new ReplicateScaleFilter((int)(dessin.getWidth()*zoom),(int)(dessin.getHeight()*zoom)))); + } + pic=createImage(new FilteredImageSource(pic.getSource(), + new CropImageFilter(coords[0],coords[1],coords[2],coords[3]))); + return toBufferedImage(pic); + } + + + public BufferedImage getSelectionImage(){ + Image pic=DrawPanel.dessin; + if (zoom!=1){ + pic=createImage(new FilteredImageSource(pic.getSource(), + new ReplicateScaleFilter((int)(dessin.getWidth()*zoom),(int)(dessin.getHeight()*zoom)))); + } + if (null!=selection){ + int x=(int)(selection.getBounds().getX()); + int y=(int)(selection.getBounds().getY()); + int width=(int)(selection.getBounds().getWidth()); + int height=(int)(selection.getBounds().getHeight()); + pic=createImage(new FilteredImageSource(pic.getSource(), + new CropImageFilter(x,y,width,height))); + } + return toBufferedImage(pic); + } +// This method returns a buffered image with the contents of an image + private BufferedImage toBufferedImage(Image image) { + if (image instanceof BufferedImage) + return (BufferedImage)image; + + // This code ensures that all the pixels in the image are loaded + image = new ImageIcon(image).getImage(); + + + // Create a buffered image with a format that's compatible with the screen + BufferedImage bimage = null; + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + try { + // Determine the type of transparency of the new buffered image + int transparency = Transparency.OPAQUE; + + // Create the buffered image + GraphicsDevice gs = ge.getDefaultScreenDevice(); + GraphicsConfiguration gc = gs.getDefaultConfiguration(); + bimage = gc.createCompatibleImage( + image.getWidth(null), image.getHeight(null), transparency); + } catch (HeadlessException e) { + // The system does not have a screen + } + + if (bimage == null) { + // Create a buffered image using the default color model + int type = BufferedImage.TYPE_INT_RGB; + bimage = new BufferedImage(image.getWidth(null), image.getHeight(null), type); + } + + // Copy image to buffered image + Graphics g = bimage.createGraphics(); + + // Paint the image onto the buffered image + g.drawImage(image, 0, 0, null); + g.dispose(); + + return bimage; + } + class PositionJViewport implements Runnable{ + JViewport jv; + Point p; + PositionJViewport(JViewport jv, Point p){ + this.jv=jv; + this.p=p; + } + public void run(){ + revalidate(); + //cadre.calculateMargin(); // TODO here is a zoom bug TODO maybe return this + // I have to add those two lines because of a bug I don't understand + // zoom 8 zoom 1 zoom 8 + // Sometimes after the method revalidate(), the left upper corner position + // wasn't correct + cadre.scrollArea.invalidate(); + cadre.scrollArea.validate(); + // End Bug + + jv.setViewPosition(p); + repaint(); + + //cadre.setZoomEnabled(true); + } + } + private void tryRecord2DMode(double a, double b){ + if (DrawPanel.record2D==DrawPanel.record2D_POLYGON){ + // FillPolygon mode + if (stackTriangle.size()==3){ + stackTriangle.remove(0); + stackTriangle.add(new Point2D.Double(a,b)); + } + else{ + stackTriangle.add(new Point2D.Double(a,b)); + } + if (stackTriangle.size()==3){ + GeneralPath gp=new GeneralPath(); + Line2D.Double ld=new Line2D.Double(stackTriangle.get(0),stackTriangle.get(1)); + gp.append(ld,false); + ld=new Line2D.Double(stackTriangle.get(1),stackTriangle.get(2)); + gp.append(ld,true); + ld=new Line2D.Double(stackTriangle.get(2),stackTriangle.get(0)); + gp.append(ld,true); + g.fill(gp); + } + } + + } + protected void startRecord2DPolygon(){ + DrawPanel.record2D=DrawPanel.record2D_POLYGON; + stackTriangle=new Vector<Point2D.Double>(); + stackTriangle.add(new Point2D.Double(tortue.corX,tortue.corY)); + } + protected void stopRecord2DPolygon(){ + DrawPanel.record2D=DrawPanel.record2D_NONE; + } +}
\ No newline at end of file diff --git a/logo/src/xlogo/kernel/InstructionBuffer.java b/logo/src/xlogo/kernel/InstructionBuffer.java new file mode 100644 index 0000000..befe0d9 --- /dev/null +++ b/logo/src/xlogo/kernel/InstructionBuffer.java @@ -0,0 +1,206 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +package xlogo.kernel; + +public class InstructionBuffer { + /** + * This integer represents the max Characters in the StringBuffer + */ + + private final static int MAX_CHARACTERS=15000; + /** + * The main StringBuffer containing instruction + */ + private StringBuffer mainBuffer; + /** + * If the main StringBuffer is very large, Xlogo could become very slow. If main StringBuffer should contain more than MAX_CHARACTERS, those characters are saved in this StringBuffer + */ + private StringBuffer stock; + + InstructionBuffer(){ + clear(); + } + InstructionBuffer(String s){ + mainBuffer=new StringBuffer(s); + stock=new StringBuffer(); + } + /** + * Inserts some instructions at the beginning of the StringBuffer mainBuffer + * @param sb: The code to insert + */ + void insertCode(StringBuffer sb){ + if (sb.length()>InstructionBuffer.MAX_CHARACTERS) { + // insert current mainBuffer to stock + if (mainBuffer.length()!=0) stock.insert(0,mainBuffer); + // Copy MAX_CHARACTERS into mainBuffer + mainBuffer=new StringBuffer(sb.substring(0, InstructionBuffer.MAX_CHARACTERS)); + // All remaining characters into stock + stock.insert(0,sb.substring(InstructionBuffer.MAX_CHARACTERS)); + } + else { + mainBuffer.insert(0, sb); + } + + } + /** + * returns the total length of the two Buffer + */ + protected int getLength(){ + return mainBuffer.length()+stock.length(); + } + /** + * Inserts the String s at the beginning of the mainBuffer + * @param s + */ + protected void insert(String s){ + mainBuffer.insert(0, s); + } + /** + * Search for the String s , first in mainBuffer and then in stock + * if it isn't found in any buffer returns -1 + * @param s: The String to search + */ + protected int indexOf(String s){ + int index=mainBuffer.indexOf(s); + if (index==-1){ + index=stock.indexOf(s); + if (index==-1) return -1; + else return index+mainBuffer.length(); + } + return index; + + } + /** + * Search for the String s , first in mainBuffer and then in stock + * if not found in any buffer returns -1 + * @param s: The String to search + */ + protected int indexOf(String s, int fromIndex){ + int index=-1; + if (fromIndex<mainBuffer.length()) index=mainBuffer.indexOf(s,fromIndex); + if (index==-1){ + int from=0; + if (fromIndex>=mainBuffer.length()) from=fromIndex-mainBuffer.length(); + index=stock.indexOf(s,from); + if (index==-1) return -1; + else return index+mainBuffer.length(); + } + return index; + + } + + + /** + * Delete all code from offset start to offset end + * @param start Start offset + * @param end End offset + */ + protected void delete(int start, int end){ + if (end<=mainBuffer.length()) mainBuffer.delete(start, end); + else { + stock.delete(0, end-mainBuffer.length()); + mainBuffer=new StringBuffer(); + transferStock(); + } + if (mainBuffer.length()==0){ + // if there are instruction in stock yet + if (stock.length()!=0) transferStock(); + } + + } + /** + * Transfers MAX_CHARCATERS from the buffer stock to mainBuffer + */ + + private void transferStock(){ + if (stock.length()>InstructionBuffer.MAX_CHARACTERS){ + mainBuffer.append(stock.substring(0, InstructionBuffer.MAX_CHARACTERS)); + stock.delete(0, InstructionBuffer.MAX_CHARACTERS); + } + else { + mainBuffer.append(stock); + stock=new StringBuffer(); + } + } + /** + * Returns next Word + * @return a String which represents the next word + */ + protected String getNextWord() { + StringBuffer mot = new StringBuffer(); + char caractere; + for (int i = 0; i < mainBuffer.length(); i++) { + caractere = mainBuffer.charAt(i); + if (caractere == ' ') { + return mot.toString(); + } else + mot.append(caractere); + if (i==mainBuffer.length()-1&& stock.length()!=0){ + transferStock(); + } + } + // System.out.println("mot: "+mot); + return mot.toString(); + } + /** + * Deletes the String mot from the mainBuffer instructions + * @param mot The string to delete + */ + protected void deleteFirstWord(String mot) { + if (mainBuffer.length() > mot.length()) + mainBuffer = mainBuffer.delete(0, mot.length() + 1); + else + mainBuffer = new StringBuffer(); + if (mainBuffer.length()==0){ + // if there are instruction in stock yet + if (stock.length()!=0) transferStock(); + } + } + /** + * Return Character at the chosen index. this methos search first in mainBuffer and then in stock + * @param index The chosen index + * @return A character + */ + protected char charAt(int index){ + if (index<mainBuffer.length()) return mainBuffer.charAt(index); + return stock.charAt(index-mainBuffer.length()); + + } + /** + * Clear the both buffers + */ + public void clear(){ + mainBuffer=new StringBuffer(); + stock=new StringBuffer(); + } + public String toString(){ + return mainBuffer.toString()+stock.toString(); +// return "MainBuffer:"+mainBuffer.toString()+":stock:"+stock.toString(); + } +} diff --git a/logo/src/xlogo/kernel/Interprete.java b/logo/src/xlogo/kernel/Interprete.java new file mode 100644 index 0000000..291674f --- /dev/null +++ b/logo/src/xlogo/kernel/Interprete.java @@ -0,0 +1,981 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +/** Title : XLogo + * Description : XLogo is an interpreter for the Logo + * programming language + * + * @author Loïc Le Coq */ +package xlogo.kernel; + +import java.util.Stack; +import java.util.HashMap; + +import xlogo.Application; +import xlogo.Logo; +import xlogo.kernel.userspace.UserSpace; +import xlogo.kernel.userspace.procedures.Procedure; + +public class Interprete +{ + + private LaunchPrimitive lanceprim; + + private Application app; + + private Kernel kernel; + + private UserSpace wp; + protected static boolean renvoi_instruction = false; + public static Stack<String> calcul = new Stack<String>(); + protected static Stack<HashMap<String, String>> stockvariable = new Stack<HashMap<String, String>>(); + protected static boolean stop = false; + protected static Stack<String> nom = new Stack<String>(); + // procédures attendant + // parmamètres à recevoir + public static Stack<String> en_cours = new Stack<String>(); + // actuellement en cours + // d'exécution + /** + * This buffer contains all instructions to execute + * + * @uml.property name="instructionBuffer" + * @uml.associationEnd multiplicity="(1 1)" + */ + private InstructionBuffer instructionBuffer = new InstructionBuffer(); + + public static StringBuffer actionInstruction = new StringBuffer(); + + protected static HashMap<String, String> locale = new HashMap<String, String>(); + // noms des variables + // locales + + protected static boolean operande = false; + protected static boolean operateur = false; + protected static boolean drapeau_ouvrante = false; + protected static boolean drapeau_fermante = false; + public static String lineNumber = ""; + + // private TreeParser tp; + /* + * public Interprete(Application cadre){ this.cadre=cadre; + * lanceprim=new LaunchPrimitive(cadre); cadre.error=false; } + */ + public Interprete(Application app) + { + this.kernel = app.getKernel(); + this.app = app; + wp = kernel.getWorkspace(); + lanceprim = new LaunchPrimitive(app, wp); + app.error = false; + } + + String execute(StringBuffer instructions) throws LogoError + { + if (!instructions.equals("")) + { + instructionBuffer.insertCode(instructions); + } + + // Object obca1,obca2,oban; + while (instructionBuffer.getLength() != 0) + { + if (app.error) // TODO && LogoError.lance : before, lance was always false, thus this branch was never taken. check this. + throw new LogoError(Logo.messages.getString("stop")); + while (app.affichage.isOnPause()) + { // Si l'on touche aux scrollbars + try + { + wait(); + } + catch (Exception e) + {} + } + // System.out.println("en_cours d'execution "+"\n"+ + // en_cours+"\n\n"); + // System.out.println("nom "+nom); + // System.out.println("calcul \n"+calcul+"\n\n"); + // System.out.println("nom "+nom.toString()+" locale "+locale+ " + // "+valeur+" stockvariable "+stockvariable); + // System.out.println("operande "+calcul+" "+operande+"debut"+instructionBuffer); + + // Is this line really interesting?? + if (instructionBuffer.getLength() == 0) + break; + // System.out.print("debut\n"+instructionBuffer+"\nfin\n------------------\n"); + String element = instructionBuffer.getNextWord(); + // System.out.println(app.affichage.getPause()+element); + + // System.out.println("/"+instructionBuffer+"/"); + + /* + * if (element=="") + * break; + *//* *********************************************** + * // si c'est une primitive ou une procedure ******* + * *********************************************** + */ + String element_minuscule = element.toLowerCase(); + /* + * Marko Zivkovic: In XLogo4Schools, a procedure is identified by its name, not by its position in a stack. + * Now, i denotes only Logo primitives. If element_minuscule is a procedure, then i will be set to -2, + * s.t. the interpreter can assume as before that it denotes something other than primitive. + * If i >= 0, then the interpreter will work just as before, using i. + */ + boolean isProcedure = wp.isExecutable(element_minuscule); + int i; + + if (Primitive.primitives.containsKey(element_minuscule) || isProcedure) + { + + // identifiant de la primitive + if (!isProcedure) + { + i = Integer.valueOf(Primitive.primitives.get(element_minuscule)).intValue() + % Primitive.PRIMITIVE_NUMBER; + } + else + { + //i = -i - 2; + i = -2; // Marko Zivkovic : i now denotes something else than a primitive : if i < -1 => isProcedure + } + // if (!calcul.empty()&&nom.isEmpty()) + // throw new + // monException(cadre,Logo.messages.getString("que_faire")+" + // "+calcul.pop() +" gdfdsf"); + // exécuter la procédure ou la primitive. + Stack<String> param = new Stack<String>(); + if (isInfixedOperator(i)) + { // Si c'est un opérateur infixé + deleteLineNumber(); + operateur = true; + operande = false; + /* + * if (drapeau_ouvrante) { drapeau_ouvrante=false; + * if (i!=32&&i!=33) throw new myException(element+" + * "+Logo.messages.getString("ne_peut_etre")); else + * param.push("0"); } + */ + // else + if (calcul.isEmpty()) + { // Si le + ou le - représente le + // signe négatif ou positif + if (i != 32 && i != 33) + throw new LogoError(element + " " + Logo.messages.getString("error.ne_peut_etre")); // d'un + // nombre + if (nom.isEmpty()) + param.push("0"); + else + { + String st = nom.peek(); + if (!testoperateur(st)) + param.push("0"); + else if ("*/".indexOf(st) > -1) + { // Si le signe - + // ou + suit un + // * ou / + instructionBuffer.deleteFirstWord(element); + if (st.equals("*")) + instructionBuffer.insert("* "); + else + instructionBuffer.insert("/ "); + if (i == 32) + return ("1"); // Si c'est un plus + else + return ("-1"); // Si c'est un moins + } + else + param.push("0"); + } + } + else if (nom.isEmpty()) + { + param.push(calcul.pop()); + } + else + { + String st = nom.peek(); + if (testoperateur(st)) + { + // System.out.println("st "+st+" element "+element+" + // "+prioriteinf(st,element)); + if (prioriteinf(st, element)) + { + param.push(calcul.pop()); + } + else + return (calcul.pop()); + } + else + param.push(calcul.pop()); + } + }// END: INFIX OPERATOR + else if (operande && i != 204) + { + checkParenthesis(); + operande = false; + break; + } // Si ce n'est pas l'opérateur de fin de parenthèse, on sort + + /* + * Example: + * To test | Formatted Form: + * fd 5 | fd 5 \l1 rt \l2 + * rt | --> The \l2 can't be removed before be + * end | sure the rt has noproblem + */ + if (!element.equals("\n")) + deleteLineNumber(); + instructionBuffer.deleteFirstWord(element); + + // Case with parenthensis + // eg (sum 3 4 5) + // eg (myProcedure 2 3 4 5) + if (drapeau_ouvrante) + { + drapeau_ouvrante = false; + int constantNumber = -1; + if (!hasGeneralForm(element_minuscule, i)) + { + if (!isProcedure) + constantNumber = kernel.primitive.parametres[i]; + else + constantNumber = wp.getExecutable(element_minuscule).nbparametre; + } + // Looking for all arguments (Number undefined) + nom.push(element); + int j = 0; + while (true) + { + /* + * This line fixed the bug for primitive or procedure + * without arguments + * eg: pr (pi+2) + */ + if (constantNumber == 0) + break; + try + { + operande = operateur = drapeau_ouvrante = false; + if (instructionBuffer.getNextWord().equals(")")) + { + if (constantNumber != -1) + { + // If the primitive or the procedure doesn't + // accept optional parameters + if (j > constantNumber) + { + throw new LogoError(Logo.messages.getString("too_much_arguments")); + } + else if (j < constantNumber) + throw new LogoError(Logo.messages.getString("pas_assez_de") + " " + nom.peek()); + } + break; + } + String a = execute(new StringBuffer()); + + param.push(a); + } + catch (LogoError e) + { + throw e; + } + j++; + } + // If It's a procedure + + if (isProcedure) + { + if (wp.isProcedureAmbiguous(element_minuscule)) + throw new LogoError(Logo.messages.getString("error.call.ambiguous.procedure.name")); + + Procedure proc = wp.getExecutable(element_minuscule); + + if (j > proc.nbparametre + proc.optVariables.size()) + throw new LogoError(Logo.messages.getString("too_much_arguments")); + else if (j < proc.nbparametre) + throw new LogoError(Logo.messages.getString("pas_assez_de") + " " + nom.peek()); + // Searching for optional arguments that are not defined + + if (j < proc.optVariables.size() + proc.nbparametre) + { + j = j - proc.nbparametre; + for (int c = j; c < proc.optVariables.size(); c++) + { + try + { + operande = operateur = drapeau_ouvrante = false; + String a = execute(proc.optVariablesExp.get(c)); + param.push(a); + } + catch (LogoError e) + { + throw e; + } + } + } + } + + } + // classic case: predefined number of arguments + else + { + drapeau_ouvrante = false; + // How many arguments for the procedure or the primitive + int nbparametre = 0; + // For primitive + if (!isProcedure) + nbparametre = kernel.primitive.parametres[i]; + // For procedure + else + nbparametre = wp.getExecutable(element_minuscule).nbparametre; + // Looking for each arguments + int j = 0; + nom.push(element); + while (j < nbparametre) + { + try + { + operande = operateur = drapeau_ouvrante = false; + + String a = execute(new StringBuffer()); + param.push(a); + j++; + } + catch (LogoError e) + { + throw e; + } + } + // System.out.println(instructionBuffer.toString()); + // System.out.println(nom+"arguments"+param); + // Looking for Optional arguments in case of procedure + if (isProcedure) + { + Procedure proc = wp.getExecutable(element_minuscule); + nbparametre = proc.optVariables.size(); + for (j = 0; j < nbparametre; j++) + { + try + { + operande = operateur = drapeau_ouvrante = false; + String a = execute(proc.optVariablesExp.get(j)); + param.push(a); + } + catch (LogoError e) + { + throw e; + } + } + } + } + + // ////////////////////////////////////////////////////////////////////////////////////////// + // System.out.println(nom+" "+"debut "+instruction+" + // fin\n"+param.toString()); + // System.out.println(nom); + nom.pop(); + if (!app.error) + lanceprim.execute(i, element_minuscule, param); + if (app.error) + break; + if (drapeau_fermante && !calcul.empty()) + { + drapeau_fermante = false; + operande = false; + return calcul.pop(); + } + + // Tester si la procédure rend quelque chose lorsqu'attendu + + if (!operande) + { + // dans le cas des primitives exec ou si + if (renvoi_instruction) + { + renvoi_instruction = false; + } + else + { + if (!nom.isEmpty() && !app.error && !nom.peek().equals("\n")) + { + if (!element.equals("\n")) + { + // If it's the end of a loop + // repeat 2 [fd 90 rt] + if (element.equals("\\")) + { + // The loop had been executed, we have to + // remove + // the loop instruction + int offset = instructionBuffer.indexOf(" \\ "); + instructionBuffer.delete(0, offset + 1); + + throw new LogoError(Logo.messages.getString("pas_assez_de") + " " + nom.peek()); + } + // (av 100) ---> OK + // av av 20 ----> Bad + if (!nom.peek().equals("(")) + throw new LogoError(element + " " + Logo.messages.getString("ne_renvoie_pas") + " " + + nom.peek()); + + } + } + } + } + else + { + // The primitive returns a word or a list. + // There's no primitive or procedure waiting for it. + if (!nom.isEmpty() && nom.peek().equals("\n")) + throw new LogoError(Logo.messages.getString("error.whattodo") + " " + calcul.peek() + " ?"); + } + } + + /* ******************************** + * / IF element IS A VARIABLE + * ******************************** + */ + else if (element.substring(0, 1).equals(":") && element.length() > 1) + { + // System.out.println(operande); + if (operande) + { + checkParenthesis(); + operande = false; + break; + } + else + deleteLineNumber(); + String value; + String variableName = element_minuscule.substring(1, element_minuscule.length()); + // If the variable isn't local + if (!locale.containsKey(variableName)) + { + // check it's a global variable + if (!wp.getGlobals().getVariables().contains(variableName)) // TODO + // CHECK + throw new LogoError(variableName + " " + Logo.messages.getString("error.novalue")); + else + value = wp.getGlobals().getValue(variableName).toString(); // TODO + // CHECK + } + // If the variable is local + else + { + value = locale.get(variableName); + } + + if (null == value) + throw new LogoError(variableName + " " + Logo.messages.getString("error.novalue")); + calcul.push(value); + operande = true; + operateur = false; + drapeau_ouvrante = false; + instructionBuffer.deleteFirstWord(element); + } + else + { + /* ***************************** + * IF element IS A NUMBER ****** + * ************************** + */ + try + { + Double.parseDouble(element); + boolean deleteEndZero = false; + if (element.endsWith(".0")) + { + deleteEndZero = true; + element = element.substring(0, element.length() - 2); + } + /* + * boolean addStartZero=false; + * if (element.startsWith(".") || element.equals("")){ + * element = "0" + element; + * addStartZero=true; + * } + */ + calcul.push(element); + if (operande) + { + checkParenthesis(); + calcul.pop(); + operande = false; + break; + } + else + deleteLineNumber(); + operande = true; + operateur = false; + drapeau_ouvrante = false; + // if (addStartZero) + // instructionBuffer.deleteFirstWord(element.substring(1)); + if (deleteEndZero) + instructionBuffer.deleteFirstWord(element + ".0"); + else + instructionBuffer.deleteFirstWord(element); + + } + catch (NumberFormatException e) + { + /* ********************************* + * IF element IS A SQUARE BRACKET [ + * OPEN + * ********************************** + */ + if (element.equals("[")) + { + + // Utilité de cette ligne? + // if (!calcul.isEmpty()&&operateur==false) break; + if (operande) + { + checkParenthesis(); + break; + } + else + deleteLineNumber(); + operande = true; + operateur = false; + drapeau_ouvrante = false; + instructionBuffer.deleteFirstWord(element); + String a = chercheListe(); + calcul.push(a); + } + /* *************************** + * IF element IS A PARENTHESIS + * OPEN + * *********************** + */ + else if (element.equals("(")) + { + if (operande) + { + checkParenthesis(); + break; + } + else + deleteLineNumber(); + drapeau_ouvrante = true; + + Interprete.en_cours.push("("); + int pos = chercheParenthese(); + if (pos == -1) + { + try + { + throw new LogoError(Logo.messages.getString("parenthese_fermante")); + } + catch (LogoError e1) + {} + } + instructionBuffer.deleteFirstWord(element); + // System.out.println("&&"+instruction); + Interprete.nom.push("("); + } + /* ********************************** + * IF element IS A WORD + * ************************** + */ + else if (element.substring(0, 1).equals("\"")) + { + try + { + String el = element.substring(1); + Double.parseDouble(el); + calcul.push(el); + } + catch (NumberFormatException e1) + { + calcul.push(element); + } + if (operande) + { + checkParenthesis(); + calcul.pop(); + operande = false; + break; + } + else + deleteLineNumber(); + operande = true; + operateur = false; + drapeau_ouvrante = false; + instructionBuffer.deleteFirstWord(element); + } + // Si c'est le mot pour + else if (element_minuscule.equals(Logo.messages.getString("pour"))) + { + instructionBuffer.deleteFirstWord(element); + if (instructionBuffer.getLength() != 0) + { + element = instructionBuffer.getNextWord(); + element_minuscule = element.toLowerCase(); + } + else + throw new LogoError(Logo.messages.getString("pas_assez_de") + " " + "\"" + + Logo.messages.getString("pour") + "\""); + if (Primitive.primitives.containsKey(element_minuscule) || wp.isExecutable(element_minuscule)) // TODO + // check + throw new LogoError(element + " " + Logo.messages.getString("existe_deja")); + else + { + String definition = Logo.messages.getString("pour") + " " + element + " "; + instructionBuffer.deleteFirstWord(element); + while (instructionBuffer.getLength() != 0) + { + element = instructionBuffer.getNextWord().toLowerCase(); + if (null == element) + break; + if (!element.substring(0, 1).equals(":") || element.length() == 1) + throw new LogoError(element + " " + Logo.messages.getString("pas_argument")); + definition += element + " "; + instructionBuffer.deleteFirstWord(element); + } + if (app.editeur.getComponent().isVisible()) + throw new LogoError(Logo.messages.getString("ferme_editeur")); + else + { + app.editeur.getComponent().setVisible(true); + app.editeur.setEditorStyledText(definition + "\n\n" + Logo.messages.getString("fin")); + } + } + } + else if (element.startsWith("\\l")) + { + if (operande) + { + break; + } + instructionBuffer.deleteFirstWord(element); + lineNumber = element + " "; + element = instructionBuffer.getNextWord(); + + } + else + { + deleteLineNumber(); + throw new LogoError(Logo.messages.getString("je_ne_sais_pas") + " " + element); + } + } + } + // System.out.println("instruction "+instruction+" calcul "+calcul); + } + /* ****************************** + * **** END OF THE MAIN LOOP + * ***************************** + */ + // S'il n'y a rien à retourner. + if (calcul.isEmpty()) + { + if (!nom.isEmpty()) + {// &&!nom.peek().equals("\n")) { + while ((!nom.isEmpty()) && nom.peek().equals("\n")) + nom.pop(); + if (!nom.isEmpty()) { throw new LogoError(Logo.messages.getString("pas_assez_de") + " " + nom.peek()); } + } + } + // Sinon on retourne la valeur contenue dans la pile de calcul. + if (!calcul.isEmpty()) + { + // S'il y a une procédure de lancer + // Ex: pour t -- 6 -- fin . Puis, av t. + if ((!nom.isEmpty()) && nom.peek().equals("\n")) + { + String up = ""; + int id = 0; + while (!nom.isEmpty() && nom.peek().equals("\n")) + { + nom.pop(); + id++; + } + if (!nom.isEmpty()) + { + up = nom.peek().toString(); + try + { + throw new LogoError(en_cours.get(en_cours.size() - id) + " " + + Logo.messages.getString("ne_renvoie_pas") + " " + up); + } + catch (LogoError e) + {} + } + else + { + try + { + throw new LogoError(Logo.messages.getString("error.whattodo") + " " + calcul.peek() + " ?"); + } + catch (LogoError e) + {} + } + /* + * } + * if (!nom.isEmpty() && nom.peek().equals("\n")) { + * up = en_cours.get(en_cours.size() - 2).toString(); + * try { + * throw new myException( en_cours.peek() + " " + * + Logo.messages.getString("ne_renvoie_pas") + * + " " + up); + * } catch (myException e) { + * } + * } else if (!nom.isEmpty()) { + * up = nom.peek().toString(); + * try { + * throw new myException( en_cours.peek() + " " + * + Logo.messages.getString("ne_renvoie_pas") + * + " " + up); + * } catch (myException e) { + * } + * } else { + * try { + * throw new myException( Logo.messages + * .getString("que_faire") + * + " " + calcul.peek() + " ?"); + * } catch (myException e) { + * }} + */ + + } + // /////////// + else + { + operande = false; + return (calcul.pop()); + } + } + return (""); + } + + private int chercheParenthese() + { // position ou s'arrete la prochaine + // parenthese + boolean continuer = true; + int of_ouvrant; + int of_fermant = 0; + int from_index_ouvrant = 1; + int from_index_fermant = 1; + while (continuer) + { + of_ouvrant = instructionBuffer.indexOf("(", from_index_ouvrant); + of_fermant = instructionBuffer.indexOf(")", from_index_fermant); + if (of_fermant == -1) + break; + if (of_ouvrant != -1 && of_ouvrant < of_fermant) + { + from_index_ouvrant = of_ouvrant + 1; + from_index_fermant = of_fermant + 1; + } + else + continuer = false; + ; + } + return of_fermant; + } + + protected String chercheListe() throws LogoError + { + String liste = "[ "; + String element = ""; + while (instructionBuffer.getLength() != 0) + { + element = instructionBuffer.getNextWord(); + // SI crochet ouvrant, on l'empile dans la pile de calcul + if (element.equals("[")) + { + calcul.push("["); + instructionBuffer.deleteFirstWord(element); + liste += "[ "; + } + + else if (element.equals("]")) + { // Si on atteint un crochet fermant + instructionBuffer.deleteFirstWord(element); + // if (((Stack)instruction.peek()).isEmpty()) instruction.pop(); + liste += "] "; + if (calcul.empty()) + { + return (liste); + } // 1er cas: rien dans la pile de calcul, on renvoie la liste + else if (!calcul.peek().toString().equals("[")) + { + return (liste); + } // 2eme cas: pas de crochet ouvrant en haut de la pile, idem + else + calcul.pop(); // 3eme cas: un crochet ouvrant en haut de + // la pile, on l'enleve + } + else + { + instructionBuffer.deleteFirstWord(element); + liste += element + " "; + } + } + throw new LogoError(Logo.messages.getString("erreur_crochet")); + } + + private boolean testoperateur(String st) + { // l'élément trouvé est-il un + // opérateur + int i = "+-*/<>=!&|".indexOf(st); + if (i == -1) + return (false); + return (true); + } + + /** + * This method compares the two operators op and str.<br> + * Cette methode teste si l'operateur op (sommet de la pile d'appel) + * est de priorite strictement inferieur a str + * + * @param op + * The first operator + * @param str + * The second operator + * @return true if op has a minor priority than str. + */ + + private boolean prioriteinf(String op, String str) + { /* + * if (parenthesefermante>0) return(false); // + * else + */ + if (isTimesDiv(str) && !isTimesDiv(op)) + return (true); + else if (isPlusMinus(str) && isLogicOperator(op)) + return (true); + else if (">=<=".indexOf(str) > -1 && "|&".indexOf(op) > -1) + return (true); + return (false); + } + + /* + * private int isProcedure(String mot) { // vérifie si mot est une + * procédure + * for (int i = 0; i < wp.getNumberOfProcedure(); i++) { + * if (wp.getProcedure(i).name.equals(mot)) + * return (i); + * } + * return (-1); + * } + */ + + protected void setWorkspace(UserSpace workspace) + { + wp = workspace; + lanceprim.setWorkspace(workspace); + } + + private boolean hasGeneralForm(String name, int i) + { + // If it's a procedure + if (wp.isExecutable(name)) + return !wp.getExecutable(name).optVariables.isEmpty(); + return kernel.primitive.generalForm[i]; + } + + private void checkParenthesis() throws LogoError + { + if (!nom.isEmpty()) + { + String name = nom.peek(); + if (name.equals("(")) { throw new LogoError(Logo.messages.getString("too_much_arguments")); + + } + } + + } + + private void deleteLineNumber() + { + lineNumber = ""; + } + + /** + * This method indicates if a primitive is an infixed operator<br> + * Infixed operators are for example: +,-,*-,/,&,>=..... + * + * @param id + * The integer identifiant for the primitive + * @return true or false if it's an infixed operator + */ + private boolean isInfixedOperator(int id) + { + boolean b1 = (29 < id) && (id < 39); + boolean b2 = (id == 273) || (id == 274); + return b1 || b2; + } + + /** + * This metods tests if the String op is a logic operator, ie a string like + * |,&,<,>,=,>=,<= + * + * @param op + * The operator to test + * @return true if op is a logic operator + */ + private boolean isLogicOperator(String op) + { + return ("|&>=<=".indexOf(op) != -1); + + } + + /** + * This metods tests if the String op is + or - + * + * @param op + * The operator to test + * @return true if op is + or - + */ + private boolean isPlusMinus(String op) + { + return (op.equals("+") || op.equals("-")); + } + + /** + * This metods tests if the String op is / or * + * + * @param op + * The operator to test + * @return true if op is * or / + */ + private boolean isTimesDiv(String op) + { + return (op.equals("*") || op.equals("/")); + } + + /** + * @return + * @uml.property name="instructionBuffer" + */ + protected InstructionBuffer getInstructionBuffer() + { + return instructionBuffer; + } +} diff --git a/logo/src/xlogo/kernel/Kernel.java b/logo/src/xlogo/kernel/Kernel.java new file mode 100644 index 0000000..108fef4 --- /dev/null +++ b/logo/src/xlogo/kernel/Kernel.java @@ -0,0 +1,211 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +package xlogo.kernel; + +import java.util.ArrayList; +import java.awt.Color; + +import xlogo.kernel.userspace.UserSpace; +import xlogo.storage.user.DrawQuality; +import xlogo.storage.workspace.Language; +/** + * Title : XLogo + * Description : XLogo is an interpreter for the Logo + * programming language + * + * @author Loïc Le Coq + */ +import xlogo.Application; + +public class Kernel +{ + protected static long chrono = 0; + + protected ArrayFlow flows = new ArrayFlow(); // Contient les + // flux de + // lecture ou + // d'écriture + + protected static boolean mode_trace = false; // true si le + // mode trace + // est + // enclenchée + // (permet de + // suivre les + // procédures) + + // interprete the user command and launch primitive and procedure + private Interprete interprete; + // For all drawing operation + // protected DrawPanel dg; + // For primitive + protected Primitive primitive = null; + private UserSpace userSpace; + private Application app; + private MP3Player mp3Player; + private MyCalculator myCalculator; + + public Kernel(Application app, UserSpace userSpace) + { + this.app = app; + this.userSpace = userSpace; + initCalculator(-1); + } + + public UserSpace getWorkspace() + { + return userSpace; + } + + public void setWorkspace(UserSpace workspace) + { + userSpace = workspace; + interprete.setWorkspace(userSpace); + } + + protected String listSearch() throws xlogo.kernel.LogoError + { + return interprete.chercheListe(); + } + + public void fcfg(Color color) + { + app.getDrawPanel().fcfg(color); + } + + public Turtle getActiveTurtle() + { + return app.getDrawPanel().tortue; + } + + public MyCalculator getCalculator() + { + return myCalculator; + } + + public void fcc(Color color) + { + app.getDrawPanel().fcc(color); + } + + public void vide_ecran() + { + app.getDrawPanel().videecran(); + } + + public void setNumberOfTurtles(int i) + { + app.getDrawPanel().setNumberOfTurtles(i); + } + + public void setDrawingQuality(DrawQuality q) + { + app.getDrawPanel().setQuality(q); + } + + public Color getScreenBackground() + { + return app.getDrawPanel().getBackgroundColor(); + } + + public void change_image_tortue(String chemin) + { + app.getDrawPanel().change_image_tortue(app, chemin); + } + + public void initGraphics() + { + app.getDrawPanel().initGraphics(); + } + + public void buildPrimitiveTreemap(Language lang) + { + primitive.buildPrimitiveTreemap(lang); + } + + public String execute(StringBuffer st) throws LogoError + { + return interprete.execute(st); + } + + protected void initCalculator(int s) + { + myCalculator = new MyCalculator(s); + + } + + public void initPrimitive() + { + primitive = new Primitive(app); + } + + public void initInterprete() + { + interprete = new Interprete(app); + } + + /** + * Returns the InstructionBuffer containing all commands to execute + */ + public InstructionBuffer getInstructionBuffer() + { + return interprete.getInstructionBuffer(); + } + + public void setMp3Player(MP3Player mp3Player) + { + this.mp3Player = mp3Player; + } + + public MP3Player getMp3Player() + { + return mp3Player; + } + + class ArrayFlow extends ArrayList<MyFlow> + { + ArrayFlow() + { + super(); + } + + private static final long serialVersionUID = 1L; + + protected int search(int id) + { + for (int i = 0; i < size(); i++) + { + if (get(i).getId() == id) + return i; + } + return -1; + } + + } +} diff --git a/logo/src/xlogo/kernel/LaunchPrimitive.java b/logo/src/xlogo/kernel/LaunchPrimitive.java new file mode 100644 index 0000000..47827cd --- /dev/null +++ b/logo/src/xlogo/kernel/LaunchPrimitive.java @@ -0,0 +1,4751 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +/** + * Title : XLogo + * Description : XLogo is an interpreter for the Logo + * programming language + * + * @author Loïc Le Coq + */ +package xlogo.kernel; + +import java.util.Stack; +import java.util.Vector; +import java.util.Iterator; +import java.util.HashMap; +import java.util.StringTokenizer; +import java.util.Calendar; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Font; +import java.awt.Rectangle; +import java.awt.image.BufferedImage; + +import javax.imageio.*; + +import java.io.*; + +import javax.swing.JOptionPane; +import javax.swing.ImageIcon; +import javax.swing.Icon; +import javax.vecmath.Point3d; + +import java.math.BigDecimal; + +import xlogo.storage.WSManager; +import xlogo.storage.global.GlobalConfig; +import xlogo.storage.user.DrawQuality; +import xlogo.storage.user.PenShape; +import xlogo.storage.user.UserConfig; +import xlogo.storage.workspace.Language; +import xlogo.utils.Utils; +import xlogo.gui.Lis; +import xlogo.gui.MyTextAreaDialog; +import xlogo.kernel.DrawPanel; +import xlogo.gui.HistoryPanel; +import xlogo.Application; +import xlogo.Logo; +import xlogo.kernel.network.*; +import xlogo.kernel.gui.*; +import xlogo.kernel.perspective.ElementPolygon; +import xlogo.kernel.perspective.ElementLine; +import xlogo.kernel.perspective.ElementPoint; +import xlogo.kernel.userspace.UserSpace; +import xlogo.kernel.userspace.files.LogoFile; +import xlogo.kernel.userspace.procedures.Procedure; +import xlogo.messages.async.history.HistoryMessenger; + +/******************************************************************************* + * When a primitive or a procedure has all arguments, LauchPrimitive executes + * the appropriate code. + ******************************************************************************/ +public class LaunchPrimitive +{ + /** + * Default Application frame + */ + private Application cadre; + /** + * Default kernel + */ + private Kernel kernel; + /** + * Default workspace + */ + private UserSpace wp; + + private Procedure procedure; + // private MathContext mc=MathContext.DECIMAL64; + /** + * This is the start for the String returned by primitive or procedure.<br> + * It is "\"" for words and "" for numbers. <br> + * <br> + * Ceci est le début de la chaine générique renvoyé par les primitives<br> + * Elle vaut "\"" pour les mots et "" pour les nombres<br> + */ + private String debut_chaine = ""; + /** + * When we launch the primitive "listentcp", we have to save workspaces + */ + //private Stack<LogoContext> savedWorkspace; + + /** + * @param cadre + * Default frame Application + * @param wp + * Default workspace + */ + public LaunchPrimitive(Application cadre, UserSpace wp) + { + this.wp = wp; + this.cadre = cadre; + this.kernel = cadre.getKernel(); + } + + /** + * Execute the primitive number "id" with the arguments contained in "param"<br> + * <ul> + * <li>if id<0: it is a procedure. <br> + * For example, if id=-3, it is procedure number -i-2=-(-3)-2=1</li> + * <li>if d>=0: it is primitive number "id"</li> + * </ul> + * + * @param id + * The number representing the procedure or the primitive + * @param param + * The Stack that contains all arguments + */ + protected void execute(int id, String element_minuscule, Stack<String> param) throws LogoError + { + UserConfig uc = WSManager.getUserConfig(); + int w = uc.getImageWidth(); + int h = uc.getImageHeight(); + Font font = WSManager.getWorkspaceConfig().getFont(); + String defaultFolder; + + // identifiant procédure ou primitive, valeur des paramètres + if (id < 0) + { + procedure = wp.getExecutable(element_minuscule); + Interprete.stockvariable.push(Interprete.locale); + Interprete.locale = new HashMap<String, String>(); + // Read local Variable + int optSize = procedure.optVariables.size(); + int normSize = procedure.variable.size(); + for (int j = 0; j < optSize + normSize; j++) + { + // Add local Variable + if (j < normSize) + { + Interprete.locale.put(procedure.variable.get(j), param.get(j)); + } // add optional variables + else + { + String value = ""; + if (j < param.size()) + value = param.get(j); + else + value = procedure.optVariablesExp.get(j - param.size()).toString(); + Interprete.locale.put(procedure.optVariables.get(j - normSize), value); + + } + } + // Add Optional variable + if (Kernel.mode_trace) + { + StringBuffer buffer = new StringBuffer(); + for (int i = 0; i < Interprete.en_cours.size(); i++) + buffer.append(" "); + buffer.append(procedure.name); + for (int i = 0; i < param.size(); i++) + buffer.append(" " + Utils.SortieTexte(param.get(i))); + String msg = buffer + "\n"; + HistoryMessenger.getInstance().dispatchMessage(msg); + } + Interprete.en_cours.push(procedure.name); + procedure.decoupe(); + // Add Procedure code in Interprete.instruction + kernel.getInstructionBuffer().insert("\n "); + kernel.getInstructionBuffer().insertCode(procedure.instr); + // System.out.println("instr " +Interprete.instruction); + // System.out.println("stock "+Interprete.stockInstruction); + // System.out.println("a"+Interprete.instruction+"a"); + Interprete.nom.push("\n"); + } + else + { + Language lang = WSManager.getInstance().getWorkspaceConfigInstance().getLanguage(); + + switch (id) + { + case 0: // av + delay(); + cadre.getDrawPanel().av(kernel.getCalculator().numberDouble(param.pop())); + break; + case 1: // re + delay(); + cadre.getDrawPanel().av(-kernel.getCalculator().numberDouble(param.pop())); + break; + case 2: // td + delay(); + cadre.getDrawPanel().td(kernel.getCalculator().numberDouble(param.pop())); + break; + case 3: // tg + delay(); + cadre.getDrawPanel().td(-kernel.getCalculator().numberDouble(param.pop())); + break; + case 4: // arithmetic.power puissance + Interprete.operande = true; + Interprete.calcul.push(kernel.getCalculator().power(param.get(0), param.get(1))); + break; + case 5: // repete controls.repeat + String liste = getList(param.get(1)); + kernel.primitive.repete(kernel.getCalculator().getInteger(param.get(0)), liste); + break; + case 6: // ve + cadre.getDrawPanel().videecran(); + break; + case 7: // ct + if (kernel.getActiveTurtle().isVisible()) + { + cadre.getDrawPanel().ct_mt(); + cadre.getDrawPanel().tortues_visibles.remove(String.valueOf(kernel.getActiveTurtle().id)); + } + kernel.getActiveTurtle().setVisible(false); + break; + case 8: // mt + if (!kernel.getActiveTurtle().isVisible()) + { + cadre.getDrawPanel().ct_mt(); + cadre.getDrawPanel().tortues_visibles.push(String.valueOf(kernel.getActiveTurtle().id)); + } + kernel.getActiveTurtle().setVisible(true); + break; + case 9: // ecris, ec + int size = param.size(); + String result = ""; + String mot; + for (int i = 0; i < size; i++) + { + String par = param.get(i).trim(); + if (isList(par)) + par = formatList(par.substring(1, par.length() - 1)); + mot = getWord(param.get(i)); + if (null == mot) + result += Utils.SortieTexte(par) + " "; + else + result += Utils.SortieTexte(mot) + " "; + } + HistoryMessenger.getInstance().dispatchLogoOutput(result + "\n"); + break; + case 10: // si // if + + liste = getList(param.get(1)); + liste = new String(Utils.decoupe(liste)); + String liste2 = null; + boolean predicat = predicat(param.get(0)); + InstructionBuffer instruction = cadre.getKernel().getInstructionBuffer(); + if (instruction.getLength() != 0) + { + try + { + String element = instruction.getNextWord(); + // System.out.println("a"+element+"a"); + if (element.startsWith("\\l")) + { + instruction.deleteFirstWord(element); + Interprete.lineNumber = element + " "; + } + if (instruction.charAt(0) == '[') + { + instruction.deleteFirstWord("["); + liste2 = getFinalList(kernel.listSearch()); + liste2 = new String(Utils.decoupe(liste2)); + } + } + catch (Exception e) + {} + } + kernel.primitive.si(predicat, liste, liste2); + Interprete.renvoi_instruction = true; + break; + case 11: // STOP + kernel.primitive.stop(); + break; + case 12: // origine + delay(); + cadre.getDrawPanel().origine(); + break; + case 13: // fpos + delay(); + String list = getFinalList(param.get(0)); + cadre.getDrawPanel().fpos(list); + break; + case 14: // fixex + delay(); + if (DrawPanel.WINDOW_MODE != DrawPanel.WINDOW_3D) + { + double x = kernel.getCalculator().numberDouble(param.get(0)); + double y = h / 2 - kernel.getActiveTurtle().corY; + cadre.getDrawPanel().fpos(x + " " + y); + } + else + cadre.getDrawPanel().fpos( + kernel.getCalculator().numberDouble(param.get(0)) + " " + kernel.getActiveTurtle().Y + + " " + kernel.getActiveTurtle().Z); + break; + case 15: // fixey + delay(); + if (DrawPanel.WINDOW_MODE != DrawPanel.WINDOW_3D) + { + double y = kernel.getCalculator().numberDouble(param.get(0)); + double x = kernel.getActiveTurtle().corX - w / 2; + cadre.getDrawPanel().fpos(x + " " + y); + } + else + cadre.getDrawPanel().fpos( + kernel.getActiveTurtle().X + " " + kernel.getCalculator().numberDouble(param.get(0)) + + " " + kernel.getActiveTurtle().Z); + break; + case 16: // fixexy + delay(); + primitive2D("drawing.fixexy"); + cadre.getDrawPanel().fpos( + kernel.getCalculator().numberDouble(param.get(0)) + " " + + kernel.getCalculator().numberDouble(param.get(1))); + break; + case 17: // fixecap + delay(); + if (DrawPanel.WINDOW_MODE != DrawPanel.WINDOW_3D) + cadre.getDrawPanel().td( + 360 - kernel.getActiveTurtle().heading + + kernel.getCalculator().numberDouble(param.pop())); + else + { + cadre.getDrawPanel().setHeading(kernel.getCalculator().numberDouble(param.pop())); + } + break; + case 18: // lc + kernel.getActiveTurtle().setPenDown(false); + break; + case 19: // bc + kernel.getActiveTurtle().setPenDown(true); + break; + case 20: // gomme + kernel.getActiveTurtle().setPenDown(true); + // if mode penerase isn't active yet + if (kernel.getActiveTurtle().couleurmodedessin.equals(kernel.getActiveTurtle().couleurcrayon)) + { + kernel.getActiveTurtle().couleurmodedessin = kernel.getActiveTurtle().couleurcrayon; + kernel.getActiveTurtle().couleurcrayon = cadre.getDrawPanel().getBackgroundColor(); + kernel.getActiveTurtle().stroke = new BasicStroke(3); // TODO + // make + // member + } + break; + case 21: // inversecrayon + kernel.getActiveTurtle().setPenDown(true); + kernel.getActiveTurtle().setPenReverse(true); + break; + case 22: // dessine + kernel.getActiveTurtle().setPenReverse(false); + kernel.getActiveTurtle().setPenDown(true); + kernel.getActiveTurtle().couleurcrayon = kernel.getActiveTurtle().couleurmodedessin; + kernel.getActiveTurtle().stroke = new BasicStroke(1); // TODO + // make + // member + break; + case 23: // somme + Interprete.operande = true; + Interprete.calcul.push(kernel.getCalculator().add(param)); + break; + + case 24: // difference + Interprete.operande = true; + Interprete.calcul.push(kernel.getCalculator().substract(param)); + break; + case 25: // arithmetic.minus moins (opposé) + Interprete.calcul.push(kernel.getCalculator().minus(param.get(0))); + Interprete.operande = true; + break; + case 26: // produit + Interprete.calcul.push(kernel.getCalculator().multiply(param)); + Interprete.operande = true; + break; + case 27: // div + Interprete.operande = true; + Interprete.calcul.push(kernel.getCalculator().divide(param)); + break; + case 28: // reste + Interprete.operande = true; + Interprete.calcul.push(kernel.getCalculator().remainder(param.get(0), param.get(1))); + break; + case 29: // retourne + kernel.primitive.retourne(param.get(0)); + break; + case 30: // * + Interprete.operande = true; + Interprete.calcul.push(kernel.getCalculator().multiply(param)); + break; + case 31: // diviser / + Interprete.operande = true; + Interprete.calcul.push(kernel.getCalculator().divide(param)); + break; + case 32: // + + Interprete.operande = true; + Interprete.calcul.push(kernel.getCalculator().add(param)); + break; + case 33: // - + Interprete.operande = true; + Interprete.calcul.push(kernel.getCalculator().substract(param)); + break; + case 34: // = + equal(param); + break; + case 35: // < + inf(param); + break; + case 36: // > + sup(param); + break; + case 37: // | + boolean b1 = predicat(param.get(0)); + boolean b2 = predicat(param.get(1)); + b1 = b1 | b2; + if (b1) + Interprete.calcul.push(Logo.messages.getString("vrai")); + else + Interprete.calcul.push(Logo.messages.getString("faux")); + Interprete.operande = true; + break; + case 38: // & + b1 = predicat(param.get(0)); + b2 = predicat(param.get(1)); + b1 = b1 & b2; + if (b1) + Interprete.calcul.push(Logo.messages.getString("vrai")); + else + Interprete.calcul.push(Logo.messages.getString("faux")); + Interprete.operande = true; + break; + case 39: // opérateur interne \n signalant une fin de + // procédure + Interprete.locale = Interprete.stockvariable.pop(); + if (Interprete.nom.peek().equals("\n")) + { + Interprete.nom.pop(); + Interprete.lineNumber = ""; + } + else + { + /* + * Example + * to bug + * av + * end + */ + throw new LogoError(Logo.messages.getString("pas_assez_de") + " " + Interprete.nom.peek()); + } + /* + * to bug [:a] | (bug 10) + * av :a | + * end | + */ + if (!Interprete.nom.isEmpty() && !Interprete.nom.peek().equals("\n") + && !Interprete.nom.peek().equals("(")) + { + if (!cadre.error) + throw new LogoError(Interprete.en_cours.peek() + " " + + Logo.messages.getString("ne_renvoie_pas") + " " + + Interprete.nom.peek().toString()); + } + if (!Interprete.en_cours.isEmpty()) + Interprete.en_cours.pop(); + break; + case 40: // opérateur interne \ signalant une fin de boucle + + LoopProperties loop = Primitive.stackLoop.peek(); + // LOOP REPEAT + if (loop.isRepeat()) + { + BigDecimal compteur = loop.getCounter(); + BigDecimal fin = loop.getEnd(); + if (compteur.compareTo(fin) < 0) + { + loop.incremente(); + Primitive.stackLoop.pop(); + Primitive.stackLoop.push(loop); + cadre.getKernel().getInstructionBuffer().insert(loop.getInstr() + Primitive.END_LOOP + " "); + } + else if (compteur.compareTo(fin) == 0) + { + Primitive.stackLoop.pop(); + } + } + // LOOP FOR or LOOP FOREACH + else if (loop.isFor() || loop.isForEach()) + { + BigDecimal inc = loop.getIncrement(); + BigDecimal compteur = loop.getCounter(); + BigDecimal fin = loop.getEnd(); + if ((inc.compareTo(BigDecimal.ZERO) == 1 && (compteur.add(inc).compareTo(fin) <= 0)) + || (inc.compareTo(BigDecimal.ZERO) == -1 && (compteur.add(inc).compareTo(fin) >= 0))) + { + loop.incremente(); + ((LoopFor) loop).AffecteVar(false); + Primitive.stackLoop.pop(); + Primitive.stackLoop.push(loop); + cadre.getKernel().getInstructionBuffer().insert(loop.getInstr() + Primitive.END_LOOP + " "); + } + else + { + ((LoopFor) loop).DeleteVar(); + Primitive.stackLoop.pop(); + } + } + // LOOP FOREVER + else if (loop.isForEver()) + { + cadre.getKernel().getInstructionBuffer().insert(loop.getInstr() + Primitive.END_LOOP + " "); + } + // LOOP FILL POLYGON + else if (loop.isFillPolygon()) + { + cadre.getDrawPanel().stopRecord2DPolygon(); + Primitive.stackLoop.pop(); + } + break; + case 41: // pos + Interprete.operande = true; + if (DrawPanel.WINDOW_MODE != DrawPanel.WINDOW_3D) + { + double a = kernel.getActiveTurtle().corX - w / 2; + double b = h / 2 - kernel.getActiveTurtle().corY; + Interprete.calcul.push("[ " + MyCalculator.teste_fin_double(a) + " " + + MyCalculator.teste_fin_double(b) + " ] "); + } + else + { + Interprete.calcul.push("[ " + kernel.getActiveTurtle().X + " " + kernel.getActiveTurtle().Y + + " " + kernel.getActiveTurtle().Z + " ] "); + + } + break; + case 42: // cap + Interprete.operande = true; + Interprete.calcul.push(MyCalculator.teste_fin_double(kernel.getActiveTurtle().heading)); + break; + case 43: // arrondi + Interprete.operande = true; + Interprete.calcul + .push(String.valueOf(Math.round(kernel.getCalculator().numberDouble(param.get(0))))); + break; + case 44: // log10 + Interprete.operande = true; + Interprete.calcul.push(kernel.getCalculator().log10(param.get(0))); + break; + case 45: // arithmetic.sin + Interprete.operande = true; + Interprete.calcul.push(kernel.getCalculator().sin(param.get(0))); + break; + case 46: // arithmetic.cos + Interprete.operande = true; + Interprete.calcul.push(kernel.getCalculator().cos(param.get(0))); + break; + case 47: // ou + ou(param); + break; + case 48: // et + et(param); + break; + case 49: // non + Interprete.operande = true; + b1 = predicat(param.get(0)); + if (b1) + Interprete.calcul.push(Logo.messages.getString("faux")); + else + Interprete.calcul.push(Logo.messages.getString("vrai")); + break; + case 50: // liste + liste = "[ "; + Interprete.operande = true; + String mot2; + for (int i = 0; i < param.size(); i++) + { + mot2 = param.get(i); + mot = getWord(param.get(i)); + if (null == mot) + { + liste += mot2; + // System.out.println("a"+mot2+"a"); + } + else + { + if (mot.equals("")) + mot = "\\v"; + liste += mot + " "; + } + } + Interprete.calcul.push(liste + "] "); + break; + case 51: // phrase + liste = "[ "; + Interprete.operande = true; + for (int i = 0; i < param.size(); i++) + { + mot = getWord(param.get(i)); + mot2 = param.get(i).trim(); + if (null == mot) + { + if (isList(mot2)) + liste += mot2.substring(1, mot2.length() - 1).trim() + " "; + else + liste += mot2 + " "; + } + else + { + if (mot.equals("")) + mot = "\\v"; + liste += mot + " "; + } + } + Interprete.calcul.push(liste + "] "); + break; + case 52: // metspremier + liste = getFinalList(param.get(1)); + Interprete.operande = true; + mot = getWord(param.get(0)); + if (null != mot && mot.equals("")) + mot = "\\v"; + if (null == mot) + { + if (!liste.equals("")) + Interprete.calcul.push("[ " + param.get(0).trim() + " " + liste.trim() + " ] "); + else + Interprete.calcul.push("[ " + param.get(0).trim() + " ] "); + } + else + { + if (!liste.equals("")) + Interprete.calcul.push("[ " + mot + " " + liste.trim() + " ] "); + else + Interprete.calcul.push("[ " + mot + " ] "); + } + break; + case 53: // metsdernier + liste = getFinalList(param.get(1)).trim(); + Interprete.operande = true; + mot = getWord(param.get(0)); + if (null != mot && mot.equals("")) + mot = "\\v"; + if (null == mot) + { // Si c'est une liste + Interprete.calcul.push(("[ " + liste).trim() + " " + param.get(0).trim() + " ] "); + + } + else + Interprete.calcul.push(("[ " + liste).trim() + " " + mot + " ] "); + + break; + case 54: // inverse liste + liste = getFinalList(param.get(0)).trim(); + Interprete.operande = true; + StringTokenizer st = new StringTokenizer(liste); + liste = " ] "; + String element = ""; + while (st.hasMoreTokens()) + { + element = st.nextToken(); + if (element.equals("[")) + element = extractList(st); + liste = " " + element + liste; + } + Interprete.calcul.push("[" + liste); + break; + case 55: // choix + Interprete.operande = true; + mot = getWord(param.get(0)); + if (null == mot) + { + liste = getFinalList(param.get(0)); + int nombre = (int) Math.floor(numberOfElements(liste) * Math.random()) + 1; + String tmp = item(liste, nombre); + if (tmp.equals("\"\\v")) + tmp = "\""; + Interprete.calcul.push(tmp); + } + else + { + int nombre = (int) Math.floor(Math.random() * getWordLength(mot)) + 1; + String str = ""; + try + { + str = itemWord(nombre, mot); + Double.parseDouble(str); + Interprete.calcul.push(str); + } + catch (NumberFormatException e1) + { + Interprete.calcul.push("\"" + str); + } + } + break; + case 56: // enleve + Interprete.operande = true; + liste = getFinalList(param.get(1)); + st = new StringTokenizer(liste); + liste = "[ "; + mot = getWord(param.get(0)); + String str; + if (null != mot && mot.equals("")) + mot = "\\v"; + if (null == mot) + mot = param.get(0).trim(); + + while (st.hasMoreTokens()) + { + str = st.nextToken(); + if (str.equals("[")) + str = extractList(st); + if (!str.equals(mot)) + liste += str + " "; + } + Interprete.calcul.push(liste.trim() + " ] "); + break; + case 57: // item + Interprete.operande = true; + mot = getWord(param.get(1)); + if (null == mot) + Interprete.calcul.push(item(getFinalList(param.get(1)), + kernel.getCalculator().getInteger(param.get(0)))); + else + { + int i = kernel.getCalculator().getInteger(param.get(0)); + if (i < 1 || i > getWordLength(mot)) + throw new LogoError(Utils.primitiveName("item") + " " + + Logo.messages.getString("n_aime_pas") + i + " " + + Logo.messages.getString("comme_parametre") + "."); + else + { + str = itemWord(i, mot); + try + { + Double.parseDouble(str); + Interprete.calcul.push(str); + } + catch (NumberFormatException e1) + { + Interprete.calcul.push("\"" + str); + } + } + } + break; + case 58: // saufdernier + Interprete.operande = true; + mot = getWord(param.get(0)); + if (null == mot) + { + liste = getFinalList(param.get(0)).trim(); + element = item(liste, numberOfElements(liste)); + int longueur = element.length(); + + if (element.startsWith("\"") || element.startsWith("[")) + longueur--; + Interprete.calcul.push("[ " + liste.substring(0, liste.length() - longueur) + "] "); + } + else if (mot.equals("")) + { + throw new LogoError(Logo.messages.getString("mot_vide")); + } + else if (getWordLength(mot) == 1) + Interprete.calcul.push("\""); + else + { + String tmp = mot.substring(0, mot.length() - 1); + if (tmp.endsWith("\\")) + tmp = tmp.substring(0, tmp.length() - 1); + try + { + Double.parseDouble(tmp); + Interprete.calcul.push(tmp); + } + catch (NumberFormatException e) + { + Interprete.calcul.push(debut_chaine + tmp); + } + } + break; + case 59: // saufpremier + Interprete.operande = true; + mot = getWord(param.get(0)); + if (null == mot) + { + liste = getFinalList(param.get(0)).trim(); + element = item(liste, 1); + int longueur = element.length(); + if (element.startsWith("\"") || element.startsWith("[")) + longueur--; + Interprete.calcul.push("[" + liste.substring(longueur, liste.length()) + " ] "); + } + else if (mot.equals("")) + { + throw new LogoError(Logo.messages.getString("mot_vide")); + } + else if (getWordLength(mot) == 1) + Interprete.calcul.push("\""); + else + { + if (!mot.startsWith("\\")) + mot = mot.substring(1); + else + mot = mot.substring(2); + try + { + Double.parseDouble(mot); + Interprete.calcul.push(mot); + } + catch (NumberFormatException e) + { + Interprete.calcul.push(debut_chaine + mot); + } + } + + break; + case 60: // dernier + Interprete.operande = true; + mot = getWord(param.get(0)); + if (null == mot) + { // Si c'est une liste + liste = getFinalList(param.get(0)); + Interprete.calcul.push(item(liste, numberOfElements(liste))); + } + else if (getWordLength(mot) == 1) + Interprete.calcul.push(debut_chaine + mot); + else + { + str = ""; + try + { + str = itemWord(getWordLength(mot), mot); + Double.parseDouble(str); + Interprete.calcul.push(str); + } + catch (NumberFormatException e1) + { + Interprete.calcul.push("\"" + str); + } + } + break; + case 61: // premier first + Interprete.operande = true; + mot = getWord(param.get(0)); + if (null == mot) + { // SI c'est une liste + liste = getFinalList(param.get(0)); + // System.out.println("b"+item(liste, 1)+"b"); + Interprete.calcul.push(item(liste, 1)); + } + else if (getWordLength(mot) == 1) + Interprete.calcul.push(debut_chaine + mot); + else + { + str = ""; + try + { + str = itemWord(1, mot); + Double.parseDouble(str); + Interprete.calcul.push(str); + } + catch (NumberFormatException e2) + { + Interprete.calcul.push("\"" + str); + } + } + break; + case 62: // compte + Interprete.operande = true; + mot = getWord(param.get(0)); + if (null == mot) + { + liste = getFinalList(param.get(0)); + Interprete.calcul.push(String.valueOf(numberOfElements(liste))); + } + else + Interprete.calcul.push(String.valueOf(getWordLength(mot))); + break; + case 63: // mot? + mot = getWord(param.get(0)); + if (null == mot) + Interprete.calcul.push(Logo.messages.getString("faux")); + else + Interprete.calcul.push(Logo.messages.getString("vrai")); + Interprete.operande = true; + break; + case 64: // nombre? + try + { + Double.parseDouble(param.get(0)); + Interprete.calcul.push(Logo.messages.getString("vrai")); + } + catch (NumberFormatException e) + { + Interprete.calcul.push(Logo.messages.getString("faux")); + } + Interprete.operande = true; + break; + case 65: // liste? + liste = param.get(0).trim(); + if (isList(liste)) + Interprete.calcul.push(Logo.messages.getString("vrai")); + else + Interprete.calcul.push(Logo.messages.getString("faux")); + Interprete.operande = true; + break; + case 66: // vide? + liste = param.get(0).trim(); + mot = getWord(param.get(0)); + if (null == mot) + { // si c'est une liste ou un nombre + liste = getFinalList(liste).trim(); + if (liste.equals("")) + Interprete.calcul.push(Logo.messages.getString("vrai")); + else + Interprete.calcul.push(Logo.messages.getString("faux")); + } + else + { // Si c'est un mot + if (mot.equals("")) + Interprete.calcul.push(Logo.messages.getString("vrai")); + else + Interprete.calcul.push(Logo.messages.getString("faux")); + } + Interprete.operande = true; + break; + case 67: // egal? + equal(param); + break; + case 68: // precede? + precede(param); + break; + case 69: // membre ? + membre(param, id); + break; + case 70: // racine arithmetic.sqrt + Interprete.operande = true; + Interprete.calcul.push(kernel.getCalculator().sqrt(param.get(0))); + break; + case 71: // membre + membre(param, id); + break; + case 72: // donne + donne(param); + Interprete.operande = false; + + break; + case 73: // locale + locale(param); + Interprete.operande = false; + break; + case 74: // donnelocale + locale(param); + donne(param); + Interprete.operande = false; + break; + case 75: // fcc + Color color = null; + if (isList(param.get(0))) + { + color = rgb(param.get(0), Utils.primitiveName("fcc")); + } + else + { + int coul = kernel.getCalculator().getInteger(param.get(0)) % DrawPanel.defaultColors.length; + if (coul < 0) + coul += DrawPanel.defaultColors.length; + color = DrawPanel.defaultColors[coul]; + } + cadre.getDrawPanel().fcc(color); + break; + case 76: // fcfg setscreencolor + color = null; + if (isList(param.get(0))) + { + color = rgb(param.get(0), Utils.primitiveName("fcfg")); + } + else + { + int coul = kernel.getCalculator().getInteger(param.get(0)) % DrawPanel.defaultColors.length; + if (coul < 0) + coul += DrawPanel.defaultColors.length; + color = DrawPanel.defaultColors[coul]; + } + cadre.getDrawPanel().fcfg(color); + break; + case 77: // hasard + Interprete.operande = true; + int i = kernel.getCalculator().getInteger(param.get(0)); + i = (int) Math.floor(Math.random() * i); + Interprete.calcul.push(String.valueOf(i)); + break; + case 78: // attends + try + { + int temps = kernel.getCalculator().getInteger(param.get(0)); + if (temps < 0) + { + String attends = Utils.primitiveName("attends"); + throw new LogoError(attends + " " + Logo.messages.getString("attend_positif")); + } + else + { + int nbsecondes = temps / 60; + int reste = temps % 60; + for (i = 0; i < nbsecondes; i++) + { + Thread.sleep(1000); + if (cadre.error) + break; + } + if (!cadre.error) + Thread.sleep(reste * 50 / 3); + } + + } + catch (InterruptedException e2) + {} + break; + case 79: // procedures + Interprete.operande = true; + Interprete.calcul.push(new String(getAllProcedures())); + break; + case 80: // effaceprocedure efp + erase(param.get(0), "procedure"); + break; + + case 81: // effacevariable + erase(param.get(0), "variable"); + break; + case 82: // effacetout erall + /* + * Marko Zivkovic: + * In XLogo4Schools, we delete all files (together with the + * procedures) and clear all variables and property lists. + */ + wp.eraseAll(); + break; + case 83: // mot + Interprete.operande = true; + result = ""; + for (i = 0; i < param.size(); i++) + { + mot = getWord(param.get(i)); + if (null == mot) + throw new LogoError(param.get(i) + " " + Logo.messages.getString("error.word")); + result += mot; + } + try + { + Double.parseDouble(result); + } + catch (NumberFormatException e) + { + result = "\"" + result; + } + Interprete.calcul.push(result); + break; + case 84: // etiquette + String par = param.get(0).trim(); + if (isList(par)) + par = formatList(par.substring(1, par.length() - 1)); + mot = getWord(param.get(0)); + if (null == mot) + cadre.getDrawPanel().etiquette(Utils.SortieTexte(par)); + else + cadre.getDrawPanel().etiquette(Utils.SortieTexte(mot)); + break; + case 85: // /trouvecouleur + if (kernel.getActiveTurtle().isVisible()) + cadre.getDrawPanel().montrecacheTortue(false); + + liste = getFinalList(param.get(0)); + Color r = cadre.getDrawPanel().guessColorPoint(liste); + Interprete.operande = true; + Interprete.calcul.push("[ " + r.getRed() + " " + r.getGreen() + " " + r.getBlue() + " ] "); + if (kernel.getActiveTurtle().isVisible()) + cadre.getDrawPanel().montrecacheTortue(true); + break; + case 86: // fenetre + cadre.getDrawPanel().setWindowMode(DrawPanel.WINDOW_CLASSIC); + break; + case 87: // enroule + cadre.getDrawPanel().setWindowMode(DrawPanel.WINDOW_WRAP); + break; + case 88: // clos + cadre.getDrawPanel().setWindowMode(DrawPanel.WINDOW_CLOSE); + break; + case 89: // videtexte + cadre.getHistoryPanel().vide_texte(); + break; + case 90: // chargeimage + BufferedImage image = null; + + primitive2D("ci"); + image = getImage(param.get(0)); + if (null != image) + cadre.getDrawPanel().chargeimage(image); + break; + case 91: // ftc, fixetaillecrayon + double nombre = kernel.getCalculator().numberDouble(param.get(0)); + if (nombre < 0) + nombre = Math.abs(nombre); + if (DrawPanel.record3D == DrawPanel.record3D_LINE || DrawPanel.record3D == DrawPanel.record3D_POINT) + { + if (kernel.getActiveTurtle().getPenWidth() != (float) nombre) + DrawPanel.poly.addToScene(); + } + kernel.getActiveTurtle().fixe_taille_crayon((float) nombre); + cadre.getDrawPanel().setStroke(kernel.getActiveTurtle().crayon); + if (DrawPanel.record3D == DrawPanel.record3D_LINE) + { + DrawPanel.poly = new ElementLine(cadre.getViewer3D(), cadre.getKernel().getActiveTurtle().getPenWidth()); + DrawPanel.poly.addVertex( + new Point3d(kernel.getActiveTurtle().X / 1000, kernel.getActiveTurtle().Y / 1000, + kernel.getActiveTurtle().Z / 1000), kernel.getActiveTurtle().couleurcrayon); + } + else if (DrawPanel.record3D == DrawPanel.record3D_POINT) + { + DrawPanel.poly = new ElementPoint(cadre.getViewer3D(), cadre.getKernel().getActiveTurtle().getPenWidth()); + } + break; + case 92: // tantque + String li1 = getList(param.get(0)); + li1 = new String(Utils.decoupe(li1)); + String li2 = getList(param.get(1)); + li2 = new String(Utils.decoupe(li2)); + String instr = "\\siwhile " + li1 + "[ " + li2 + "] "; + LoopWhile bp = new LoopWhile(BigDecimal.ONE, BigDecimal.ZERO, BigDecimal.ONE, instr); + Primitive.stackLoop.push(bp); + cadre.getKernel().getInstructionBuffer().insert(instr + Primitive.END_LOOP + " "); + + break; + case 93: // lis + liste = getFinalList(param.get(0)); + mot = getWord(param.get(1)); + if (null == mot) + throw new LogoError(Logo.messages.getString("error.word")); + java.awt.FontMetrics fm = cadre.getFrame().getGraphics().getFontMetrics(font); + int longueur = fm.stringWidth(liste) + 100; + Lis lis = new Lis(liste, longueur); + while (lis.isVisible()) + { + try + { + Thread.sleep(50); + } + catch (InterruptedException e) + {} + } + param = new Stack<String>(); + param.push("\"" + mot); + String phrase = lis.getText(); + // phrase="[ "+Logo.rajoute_backslash(phrase)+" ] "; + StringBuffer tampon = new StringBuffer(); + for (int j = 0; j < phrase.length(); j++) + { + char c = phrase.charAt(j); + if (c == '\\') + tampon.append("\\\\"); + else + tampon.append(c); + } + int offset = tampon.indexOf(" "); + if (offset != -1) + { + tampon.insert(0, "[ "); + tampon.append(" ] "); + } + else + { + try + { + Double.parseDouble(phrase); + } + catch (NumberFormatException e) + { + tampon.insert(0, "\""); + } + } + phrase = new String(tampon); + param.push(phrase); + donne(param); + String texte = liste + "\n" + phrase; + HistoryMessenger.getInstance().dispatchComment(Utils.SortieTexte(texte) + "\n"); + cadre.focus_Commande(); + lis.dispose(); + cadre.focus_Commande(); + break; + case 94: // touche? + Interprete.operande = true; + if (cadre.getCar() != -1) + Interprete.calcul.push(Logo.messages.getString("vrai")); + else + Interprete.calcul.push(Logo.messages.getString("faux")); + break; + case 95: // siwhile --> Evalue l'expression test du while + liste = getFinalList(param.get(1)); + boolean pred = predicat(param.get(0)); + kernel.primitive.whilesi(pred, liste); + break; + case 96: // liscar + while (cadre.getCar() == -1) + { + try + { + Thread.sleep(100); + } + catch (InterruptedException e) + {} + //if (LogoError.lance) //TODO this was always false + // break; + } + Interprete.calcul.push(String.valueOf(cadre.getCar())); + Interprete.operande = true; + cadre.setCar(-1); + break; + case 97: // remplis + cadre.getDrawPanel().remplis(); + break; + case 98: // point + if (kernel.getActiveTurtle().isVisible()) + cadre.getDrawPanel().montrecacheTortue(false); + + cadre.getDrawPanel().point(getFinalList(param.get(0))); + if (kernel.getActiveTurtle().isVisible()) + cadre.getDrawPanel().montrecacheTortue(true); + break; + case 99: // vers=towards vers + + Interprete.operande = true; + if (DrawPanel.WINDOW_MODE != DrawPanel.WINDOW_3D) + { + double angle = cadre.getDrawPanel().vers2D(getFinalList(param.get(0))); + Interprete.calcul.push(MyCalculator.teste_fin_double(angle)); + } + else + { + double[] orientation = cadre.getDrawPanel().vers3D(getFinalList(param.get(0))); + Interprete.calcul.push("[ " + orientation[0] + " " + orientation[1] + " " + orientation[2] + + " ] "); + } + break; + case 100: // distance + Interprete.operande = true; + double distance = cadre.getDrawPanel().distance(getFinalList(param.get(0))); + Interprete.calcul.push(MyCalculator.teste_fin_double(distance)); + break; + case 101: // couleurcrayon + Interprete.operande = true; + Interprete.calcul.push("[ " + kernel.getActiveTurtle().couleurcrayon.getRed() + " " + + kernel.getActiveTurtle().couleurcrayon.getGreen() + " " + + kernel.getActiveTurtle().couleurcrayon.getBlue() + " ] "); + break; + case 102: // couleurfond + Interprete.operande = true; + color = cadre.getDrawPanel().getBackgroundColor(); + Interprete.calcul.push("[ " + color.getRed() + " " + color.getGreen() + " " + color.getBlue() + + " ] "); + break; + case 103: // bc? + Interprete.operande = true; + if (kernel.getActiveTurtle().isPenDown()) + Interprete.calcul.push(Logo.messages.getString("vrai")); + else + Interprete.calcul.push(Logo.messages.getString("faux")); + break; + case 104: // visible? + Interprete.operande = true; + if (kernel.getActiveTurtle().isVisible()) + Interprete.calcul.push(Logo.messages.getString("vrai")); + else + Interprete.calcul.push(Logo.messages.getString("faux")); + break; + case 105: // prim? + Interprete.operande = true; + mot = getWord(param.get(0)); + if (null == mot) + throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word")); + if (Primitive.primitives.containsKey(mot)) + Interprete.calcul.push(Logo.messages.getString("vrai")); + else + Interprete.calcul.push(Logo.messages.getString("faux")); + break; + case 106: // proc? + Interprete.operande = true; + mot = getWord(param.get(0)); + if (wp.isExecutable(mot)) + Interprete.calcul.push(Logo.messages.getString("vrai")); + else + Interprete.calcul.push(Logo.messages.getString("faux")); + break; + case 107: // exec + mot = getWord(param.get(0)); + if (null == mot) + { + mot = getList(param.get(0).trim()); + mot = new String(Utils.decoupe(mot)); + } + else + mot = mot + " "; + cadre.getKernel().getInstructionBuffer().insert(mot); + Interprete.renvoi_instruction = true; + break; + case 108: // catalogue + defaultFolder = uc.getDefaultFolder(); + str = Utils.SortieTexte(defaultFolder); + File f = new File(str); + String fichier = ""; + String dossier = ""; + int nbdossier = 0; + int nbfichier = 0; + String[] l = f.list(); + for (i = 0; i < l.length; i++) + { + if ((new File(str + File.separator + l[i])).isDirectory()) + { + nbdossier++; + if (nbdossier % 5 == 0) + dossier += l[i] + "\n"; + else + dossier += l[i] + " "; + } + else + { + nbfichier++; + if (nbfichier % 5 == 0) + fichier += l[i] + "\n"; + else + fichier += l[i] + " "; + } + } + texte = ""; + if (!dossier.equals("")) + texte += Logo.messages.getString("repertoires") + ":\n" + dossier + "\n"; + if (!fichier.equals("")) + texte += Logo.messages.getString("fichiers") + ":\n" + fichier + "\n"; + HistoryMessenger.getInstance().dispatchComment(texte); + break; + case 109: // frepertoire + liste = getWord(param.get(0)); + if (null == liste) + throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word")); + String chemin = Utils.SortieTexte(liste); + if ((new File(chemin)).isDirectory() && !chemin.startsWith("..")) + { + defaultFolder = Utils.rajoute_backslash(chemin); + uc.setDefaultFolder(defaultFolder); + } + else + throw new LogoError(liste + " " + Logo.messages.getString("erreur_pas_repertoire")); + break; + case 110: // repertoire + Interprete.operande = true; + defaultFolder = uc.getDefaultFolder(); + Interprete.calcul.push("\"" + defaultFolder); + break; + case 111: // sauve + mot = getWord(param.get(0)); + if (null == mot) + throw new LogoError(Logo.messages.getString("error.word")); + liste = getFinalList(param.get(1)); + st = new StringTokenizer(liste); + Stack<String> pile = new Stack<String>(); + while (st.hasMoreTokens()) + pile.push(st.nextToken()); + saveProcedures(mot, pile); + break; + case 112: // sauved + mot = getWord(param.get(0)); + if (null == mot) + throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word")); + saveProcedures(mot, null); + break; + case 113: // ramene load + mot = getWord(param.get(0)); + if (null == mot) + throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word")); + defaultFolder = uc.getDefaultFolder(); + String path = Utils.SortieTexte(defaultFolder) + File.separator + mot; + String fileName = wp.makeUniqueFileName(mot); + try + { + String txt = Utils.readLogoFile(path); // TODO this + // does not + // work... + // should + // load from + // path.... + // later: + // looks ok, + // not? + wp.createFile(fileName); + wp.writeFileText(fileName, txt); + } + catch (IOException e1) + { + throw new LogoError(Logo.messages.getString("error.iolecture")); + } + + break; + case 114: // pi + Interprete.operande = true; + Interprete.calcul.push(kernel.getCalculator().pi()); + break; + case 115: // tangente arithmetic.tan + Interprete.operande = true; + + Interprete.calcul.push(kernel.getCalculator().tan(param.get(0))); + + break; + case 116: // acos + + Interprete.calcul.push(kernel.getCalculator().acos(param.get(0))); + Interprete.operande = true; + break; + case 117: // asin + + Interprete.calcul.push(kernel.getCalculator().asin(param.get(0))); + Interprete.operande = true; + break; + case 118: // atan + Interprete.calcul.push(kernel.getCalculator().atan(param.get(0))); + Interprete.operande = true; + break; + case 119: // vrai + Interprete.operande = true; + Interprete.calcul.push(Logo.messages.getString("vrai")); + break; + case 120: // faux + Interprete.operande = true; + Interprete.calcul.push(Logo.messages.getString("faux")); + break; + case 121: // forme + primitive2D("turtle.forme"); + Interprete.operande = true; + Interprete.calcul.push(String.valueOf(kernel.getActiveTurtle().getShape())); + break; + case 122: // fixeforme setshape + primitive2D("turtle.fforme"); + i = kernel.getCalculator().getInteger(param.get(0)) % 7; + if (kernel.getActiveTurtle().id == 0) + { + uc.setActiveTurtle(i); + } + chemin = "tortue" + i + ".png"; + kernel.change_image_tortue(chemin); + break; + case 123: // definis workspace.define + define(param); // Method extracted by Marko Zivkovic + // 21.6.2013 + break; + + case 124: // tortue + Interprete.operande = true; + Interprete.calcul.push(String.valueOf(kernel.getActiveTurtle().id)); + break; + case 125: // tortues + Interprete.operande = true; + String li = "[ "; + for (i = 0; i < cadre.getDrawPanel().tortues.length; i++) + { + if (null != cadre.getDrawPanel().tortues[i]) + li += String.valueOf(i) + " "; + } + li += "]"; + Interprete.calcul.push(li); + break; + case 126: // fixetortue + try + { + i = Integer.parseInt(param.get(0)); + if (i > -1 && i < uc.getMaxTurtles()) + { + if (null == cadre.getDrawPanel().tortues[i]) + { + cadre.getDrawPanel().tortues[i] = new Turtle(cadre); + cadre.getDrawPanel().tortues[i].id = i; + cadre.getDrawPanel().tortues[i].setVisible(false); + } + cadre.getDrawPanel().tortue = cadre.getDrawPanel().tortues[i]; + cadre.getDrawPanel().setStroke(kernel.getActiveTurtle().crayon); + String police = cadre.getDrawPanel().getGraphicsFont().getName(); + cadre.getDrawPanel().setGraphicsFont( + new java.awt.Font(police, java.awt.Font.PLAIN, kernel.getActiveTurtle().police)); + + } + else + { + throw new LogoError(Logo.messages.getString("tortue_inaccessible")); + } + } + catch (NumberFormatException e) + { + kernel.getCalculator().getInteger(param.get(0)); + } + break; + case 127: // police + Interprete.operande = true; + Interprete.calcul.push(String.valueOf(kernel.getActiveTurtle().police)); + break; + case 128: // fixetaillepolice + int taille = kernel.getCalculator().getInteger(param.get(0)); + kernel.getActiveTurtle().police = taille; + Font police = font; + cadre.getDrawPanel().setGraphicsFont(police.deriveFont((float) kernel.getActiveTurtle().police)); + + break; + case 129: // tuetortue + try + { + id = Integer.parseInt(param.get(0)); + if (id > -1 && id < uc.getMaxTurtles()) + { + // On compte le nombre de tortues à l'écran + int compteur = 0; + int premier_dispo = -1; + for (i = 0; i < uc.getMaxTurtles(); i++) + { + if (null != cadre.getDrawPanel().tortues[i]) + { + if (i != id && premier_dispo == -1) + premier_dispo = i; + compteur++; + } + } + // On vérifie que ce n'est pas la seule tortue + // dispopnible: + if (null != cadre.getDrawPanel().tortues[id]) + { + if (compteur > 1) + { + int tortue_utilisee = kernel.getActiveTurtle().id; + cadre.getDrawPanel().tortue = cadre.getDrawPanel().tortues[id]; + cadre.getDrawPanel().ct_mt(); + cadre.getDrawPanel().tortue = cadre.getDrawPanel().tortues[tortue_utilisee]; + cadre.getDrawPanel().tortues[id] = null; + if (cadre.getDrawPanel().tortues_visibles.search(String.valueOf(id)) > 0) + cadre.getDrawPanel().tortues_visibles.remove(String.valueOf(id)); + if (kernel.getActiveTurtle().id == id) + { + cadre.getDrawPanel().tortue = cadre.getDrawPanel().tortues[premier_dispo]; + cadre.getDrawPanel().setStroke(kernel.getActiveTurtle().crayon); // on + // adapte + // le + // nouveau + // crayon + str = cadre.getDrawPanel().getGraphicsFont().getName(); + cadre.getDrawPanel().setFont( + new java.awt.Font(str, java.awt.Font.PLAIN, + kernel.getActiveTurtle().police)); + + } + } + else + { + throw new LogoError(Logo.messages.getString("seule_tortue_dispo")); + } + } + } + } + catch (NumberFormatException e) + { + kernel.getCalculator().getInteger(param.get(0)); + } + break; + case 130: // sequence + liste = getFinalList(param.get(0)); + cadre.getSon().cree_sequence(Utils.decoupe(liste).toString()); + break; + case 131: // instrument + Interprete.operande = true; + Interprete.calcul.push(String.valueOf(cadre.getSon().getInstrument())); + break; + case 132: // fixeinstrument + i = kernel.getCalculator().getInteger(param.get(0)); + cadre.getSon().setInstrument(i); + + break; + case 133: // joue + cadre.getSon().joue(); + break; + case 134: // effacesequence + cadre.getSon().efface_sequence(); + break; + case 135: // indexsequence + Interprete.operande = true; + double d = (double) cadre.getSon().getTicks() / 64; + Interprete.calcul.push(MyCalculator.teste_fin_double(d)); + + break; + case 136: // fixeindexsequence + i = kernel.getCalculator().getInteger(param.get(0)); + cadre.getSon().setTicks(i * 64); + break; + case 137:// fpt + i = kernel.getCalculator().getInteger(param.get(0)); + cadre.getHistoryPanel().getDsd().fixepolice(i); + break; + case 138: // ptexte + Interprete.operande = true; + Interprete.calcul.push(String.valueOf(cadre.getHistoryPanel().police())); + break; + case 139: // fct,fixecouleurtexte + if (isList(param.get(0))) + { + cadre.getHistoryPanel().getDsd().fixecouleur(rgb(param.get(0), Utils.primitiveName("fct"))); + } + else + { + int coul = kernel.getCalculator().getInteger(param.get(0)) % DrawPanel.defaultColors.length; + if (coul < 0) + coul += DrawPanel.defaultColors.length; + cadre.getHistoryPanel().getDsd().fixecouleur(DrawPanel.defaultColors[coul]); + } + break; + case 140: // couleurtexte + Interprete.operande = true; + Color c = cadre.getHistoryPanel().getCouleurtexte(); + Interprete.calcul.push("[ " + c.getRed() + " " + c.getGreen() + " " + c.getBlue() + " ] "); + break; + case 141: // lissouris readmouse + while (!cadre.getDrawPanel().get_lissouris()) + { + try + { + Thread.sleep(100); + } + catch (InterruptedException e) + {} + //if (LogoError.lance) //TODO Marko: this was always false + // break; + } + Interprete.calcul.push(String.valueOf(cadre.getDrawPanel().get_bouton_souris())); + Interprete.operande = true; + break; + case 142: // possouris + Interprete.calcul.push(cadre.getDrawPanel().get_possouris()); + Interprete.operande = true; + break; + case 143: // msg message + liste = getFinalList(param.get(0)); + st = new StringTokenizer(liste); // On + // découpe + // le + // message + // en + // tranche + // de + // longueurs + // acceptables + fm = cadre.getFrame().getGraphics().getFontMetrics(font); + liste = ""; + String buf = ""; + while (st.hasMoreTokens()) + { + buf += st.nextToken() + " "; + if (fm.stringWidth(buf) > 200) + { + liste += buf + "\n"; + buf = ""; + } + } + liste += buf; + liste = Utils.SortieTexte(liste); + + MyTextAreaDialog jt = new MyTextAreaDialog(liste, cadre.getHistoryPanel().getDsd()); + ImageIcon icone = new ImageIcon(Utils.class.getResource("icone.png")); + JOptionPane.showMessageDialog(cadre.getFrame(), jt, "", JOptionPane.INFORMATION_MESSAGE, + (Icon) icone); + break; + case 144: // date + Interprete.operande = true; + Calendar cal = Calendar.getInstance(lang.getLocale()); + int jour = cal.get(Calendar.DAY_OF_MONTH); + int mois = cal.get(Calendar.MONTH) + 1; + int annee = cal.get(Calendar.YEAR); + Interprete.calcul.push("[ " + jour + " " + mois + " " + annee + " ] "); + break; + case 145: // heure + Interprete.operande = true; + cal = Calendar.getInstance(lang.getLocale()); + int heure = cal.get(Calendar.HOUR_OF_DAY); + int minute = cal.get(Calendar.MINUTE); + int seconde = cal.get(Calendar.SECOND); + Interprete.calcul.push("[ " + heure + " " + minute + " " + seconde + " ] "); + break; + case 146: // temps + Interprete.operande = true; + long heure_actuelle = Calendar.getInstance().getTimeInMillis(); + Interprete.calcul.push(String.valueOf((heure_actuelle - uc.getHeure_demarrage()) / 1000)); + break; + case 147: // debuttemps + int temps = kernel.getCalculator().getInteger(param.get(0)); + Kernel.chrono = Calendar.getInstance().getTimeInMillis() + 1000 * temps; + break; + case 148: // fintemps? + Interprete.operande = true; + if (Calendar.getInstance().getTimeInMillis() > Kernel.chrono) + Interprete.calcul.push(Logo.messages.getString("vrai")); + else + Interprete.calcul.push(Logo.messages.getString("faux")); + break; + case 149: // fnp fixenompolice + int int_police = kernel.getCalculator().getInteger(param.get(0)); + cadre.getDrawPanel().police_etiquette = int_police % UserConfig.fontes.length; + break; + case 150: // np nompolice + Interprete.operande = true; + Interprete.calcul.push("[ " + cadre.getDrawPanel().police_etiquette + " [ " + + UserConfig.fontes[cadre.getDrawPanel().police_etiquette].getFontName() + " ] ] "); + break; + case 151: // fnpt fixenompolicetexte + int_police = kernel.getCalculator().getInteger(param.get(0)); + HistoryPanel.fontPrint = int_police % UserConfig.fontes.length; + cadre.getHistoryPanel().getDsd().fixenompolice(int_police); + + break; + case 152: // npt nompolicetexte + Interprete.operande = true; + Interprete.calcul.push("[ " + HistoryPanel.fontPrint + " [ " + + UserConfig.fontes[HistoryPanel.fontPrint].getFontName() + " ] ] "); + break; + case 153: // listeflux + liste = "[ "; + for (MyFlow flow : kernel.flows) + { + liste += "[ " + flow.getId() + " " + flow.getPath() + " ] "; + } + liste += "] "; + Interprete.operande = true; + Interprete.calcul.push(liste); + break; + case 154: // lisligneflux + try + { + int ident = kernel.getCalculator().getInteger(param.get(0)); + int index = kernel.flows.search(ident); + if (index == -1) + throw new LogoError(Logo.messages.getString("flux_non_disponible") + " " + ident); + MyFlow flow = kernel.flows.get(index); + MyFlowReader flowReader; + // If the flow is a writable flow, throw error + if (flow.isWriter()) + throw new LogoError(Logo.messages.getString("flux_lecture")); + // else if the flow is a readable flow, convert to + // MyFlowReader + else if (flow.isReader()) + { + flowReader = ((MyFlowReader) flow); + } + // else the flow isn't yet defined, initialize + else + flowReader = new MyFlowReader(flow); + + if (flowReader.isFinished()) + throw new LogoError(Logo.messages.getString("fin_flux") + " " + ident); + // Reading line + String line = flowReader.readLine(); + if (null == line) + { + flow.setFinished(true); + throw new LogoError(Logo.messages.getString("fin_flux") + " " + ident); + } + Interprete.operande = true; + Interprete.calcul.push("[ " + Utils.decoupe(line.trim()) + " ] "); + kernel.flows.set(index, flowReader); + } + catch (FileNotFoundException e1) + { + throw new LogoError(Logo.messages.getString("error.iolecture")); + } + catch (IOException e2) + {} + break; + case 155: // liscaractereflux + try + { + int ident = kernel.getCalculator().getInteger(param.get(0)); + int index = kernel.flows.search(ident); + if (index == -1) + throw new LogoError(Logo.messages.getString("flux_non_disponible") + " " + ident); + MyFlow flow = kernel.flows.get(index); + MyFlowReader flowReader; + // If the flow is a writable flow, throw error + if (flow.isWriter()) + throw new LogoError(Logo.messages.getString("flux_lecture")); + // else if the flow is reader, convert to MyFlowReader + else if (flow.isReader()) + { + flowReader = ((MyFlowReader) flow); + } + // else the flow isn't yet defined, initialize + else + flowReader = new MyFlowReader(flow); + + if (flowReader.isFinished()) + throw new LogoError(Logo.messages.getString("fin_flux") + " " + ident); + + int character = ((MyFlowReader) flow).readChar(); + if (character == -1) + { + flow.setFinished(true); + throw new LogoError(Logo.messages.getString("fin_flux") + " " + ident); + } + Interprete.operande = true; + String car = String.valueOf(character); + if (car.equals("\\")) + car = "\\\\"; + Interprete.calcul.push(car); + kernel.flows.set(index, flowReader); + } + catch (FileNotFoundException e1) + { + throw new LogoError(Logo.messages.getString("error.iolecture")); + } + catch (IOException e2) + {} + break; + case 156: // ecrisligneflux + try + { + int ident = kernel.getCalculator().getInteger(param.get(0)); + int index = kernel.flows.search(ident); + liste = getFinalList(param.get(1)); + if (index == -1) + throw new LogoError(Logo.messages.getString("flux_non_disponible") + " " + ident); + MyFlow flow = kernel.flows.get(index); + MyFlowWriter flowWriter; + // If the flow is a readable flow, throw an error + if (flow.isReader()) + throw new LogoError(Logo.messages.getString("flux_ecriture")); + // Else if the flow is a writable flow , convert to + // MrFlowWriter + else if (flow.isWriter()) + flowWriter = (MyFlowWriter) flow; + // Else the flow isn't defined yet, initialize + else + flowWriter = new MyFlowWriter(flow); + + // System.out.println(flow.isReader()+" "+flow.isWriter()); + // Write the line + flowWriter.write(Utils.SortieTexte(liste)); + kernel.flows.set(index, flowWriter); + } + catch (FileNotFoundException e1) + {} + catch (IOException e2) + {} + break; + case 157: // finficher? + try + { + int ident = kernel.getCalculator().getInteger(param.get(0)); + int index = kernel.flows.search(ident); + if (index == -1) + throw new LogoError(Logo.messages.getString("flux_non_disponible") + " " + ident); + else + { + MyFlow flow = kernel.flows.get(index); + MyFlowReader flowReader = null; + // If the flow isn't defined yet, initialize + if (!flow.isWriter() && !flow.isReader()) + { + flowReader = new MyFlowReader(flow); + } + else if (flow.isReader()) + flowReader = (MyFlowReader) flow; + if (null != flowReader) + { + if (flow.isFinished()) + { + Interprete.operande = true; + Interprete.calcul.push(Logo.messages.getString("vrai")); + } + else + { + int read = flowReader.isReadable(); + if (read == -1) + { + Interprete.operande = true; + Interprete.calcul.push(Logo.messages.getString("vrai")); + flow.setFinished(true); + } + else + { + Interprete.operande = true; + Interprete.calcul.push(Logo.messages.getString("faux")); + } + } + } + else + throw new LogoError(Logo.messages.getString("flux_lecture")); + } + } + catch (FileNotFoundException e1) + {} + catch (IOException e2) + {} + break; + case 158: // ouvreflux + mot = getWord(param.get(1)); + if (null == mot) + throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word")); + defaultFolder = uc.getDefaultFolder(); + liste = Utils.SortieTexte(defaultFolder) + File.separator + Utils.SortieTexte(mot); + int ident = kernel.getCalculator().getInteger(param.get(0)); + if (kernel.flows.search(ident) == -1) + kernel.flows.add(new MyFlow(ident, liste, false)); + else + throw new LogoError(ident + " " + Logo.messages.getString("flux_existant")); + break; + case 159: // fermeflux + try + { + ident = kernel.getCalculator().getInteger(param.get(0)); + int index = kernel.flows.search(ident); + if (index == -1) + throw new LogoError(Logo.messages.getString("flux_non_disponible") + " " + ident); + MyFlow flow = kernel.flows.get(index); + // If the flow is a readable flow + if (flow.isReader()) + ((MyFlowReader) flow).close(); + // Else if it's a writable flow + else if (flow.isWriter()) + ((MyFlowWriter) flow).close(); + kernel.flows.remove(index); + } + catch (IOException e2) + {} + break; + case 160: // ajouteligneflux + try + { + ident = kernel.getCalculator().getInteger(param.get(0)); + int index = kernel.flows.search(ident); + liste = getFinalList(param.get(1)); + if (index == -1) + throw new LogoError(Logo.messages.getString("flux_non_disponible") + " " + ident); + MyFlow flow = kernel.flows.get(index); + MyFlowWriter flowWriter; + // If the flow is a readable flow, throw an error + if (flow.isReader()) + throw new LogoError(Logo.messages.getString("flux_ecriture")); + // Else if the flow is a writable flow , convert to + // MrFlowWriter + else if (flow.isWriter()) + flowWriter = (MyFlowWriter) flow; + // Else the flow isn't defined yet, initialize + else + flowWriter = new MyFlowWriter(flow); + + // Write the line + flowWriter.append(Utils.SortieTexte(liste)); + kernel.flows.set(index, flowWriter); + } + catch (FileNotFoundException e1) + {} + catch (IOException e2) + {} + break; + case 161: // souris? + Interprete.operande = true; + if (cadre.getDrawPanel().get_lissouris()) + Interprete.calcul.push(Logo.messages.getString("vrai")); + else + Interprete.calcul.push(Logo.messages.getString("faux")); + break; + case 162: // variables + Interprete.operande = true; + Interprete.calcul.push(new String(getAllVariables())); + break; + case 163: // chose thing + mot = getWord(param.get(0)); + if (null == mot) + { + throw new LogoError(Logo.messages.getString("error.word")); + } // si c'est une liste + else if (debut_chaine.equals("")) { throw new LogoError( + Logo.messages.getString("erreur_variable")); } // si + // c'est + // un + // nombre + Interprete.operande = true; + String value; + mot = mot.toLowerCase(); + if (!Interprete.locale.containsKey(mot)) + { + if (!wp.getGlobals().getVariables().contains(mot)) + throw new LogoError(mot + " " + Logo.messages.getString("erreur_variable")); + else + value = wp.getGlobals().getValue(mot); + } + else + { + value = Interprete.locale.get(mot); + } + if (null == value) + throw new LogoError(mot + " " + Logo.messages.getString("erreur_variable")); + Interprete.calcul.push(value); + break; + case 164: // nettoie + cadre.getDrawPanel().nettoie(); + break; + case 165: // tape + par = param.get(0).trim(); + if (isList(par)) + par = formatList(par.substring(1, par.length() - 1)); + mot = getWord(param.get(0)); + if (null == mot) + HistoryMessenger.getInstance().dispatchLogoOutput(Utils.SortieTexte(par)); + else + HistoryMessenger.getInstance().dispatchLogoOutput(Utils.SortieTexte(mot)); + break; + case 166: // cercle + cadre.getDrawPanel().circle((kernel.getCalculator().numberDouble(param.pop()))); + break; + case 167: // arc + cadre.getDrawPanel().arc(kernel.getCalculator().numberDouble(param.get(0)), + kernel.getCalculator().numberDouble(param.get(1)), + kernel.getCalculator().numberDouble(param.get(2))); + break; + case 168: // rempliszone + cadre.getDrawPanel().rempliszone(); + break; + case 169: // animation + cadre.getDrawPanel().setAnimation(true); + Interprete.operande = false; + break; + case 170: // rafraichis + if (DrawPanel.classicMode == DrawPanel.MODE_ANIMATION) + { + cadre.getDrawPanel().refresh(); + } + break; + + case 171: // tailledessin + Interprete.operande = true; + StringBuffer sb = new StringBuffer(); + sb.append("[ "); + sb.append(w); + sb.append(" "); + sb.append(h); + sb.append(" ] "); + Interprete.calcul.push(new String(sb)); + break; + case 172: // quotient + Interprete.operande = true; + Interprete.calcul.push(kernel.getCalculator().quotient(param.get(0), param.get(1))); + break; + case 173: // entier? + Interprete.operande = true; + double ent = kernel.getCalculator().numberDouble(param.get(0)); + if ((int) ent == ent) + Interprete.calcul.push(Logo.messages.getString("vrai")); + else + Interprete.calcul.push(Logo.messages.getString("faux")); + break; + case 174: // fixeseparation + nombre = kernel.getCalculator().numberDouble(param.get(0)); + if (nombre < 0 || nombre > 1) + throw new LogoError(nombre + " " + Logo.messages.getString("entre_zero_un")); + cadre.drawingAndHistory.setResizeWeight(nombre); + cadre.drawingAndHistory.setDividerLocation(nombre); + break; + case 175: // separation + Interprete.operande = true; + Interprete.calcul.push(MyCalculator.teste_fin_double(cadre.drawingAndHistory.getResizeWeight())); + break; + case 176: // tronque + Interprete.operande = true; + Interprete.calcul.push(kernel.getCalculator().truncate(param.get(0))); + break; + case 177: // trace + Kernel.mode_trace = true; + Interprete.operande = false; + break; + case 178:// changedossier + Interprete.operande = false; + mot = getWord(param.get(0)); + if (null == mot) + throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word")); + chemin = ""; + defaultFolder = uc.getDefaultFolder(); + if (defaultFolder.endsWith(File.separator)) + chemin = Utils.SortieTexte(defaultFolder + mot); + else + chemin = Utils.SortieTexte(defaultFolder + Utils.rajoute_backslash(File.separator) + mot); + if ((new File(chemin)).isDirectory()) + { + try + { + defaultFolder = Utils.rajoute_backslash((new File(chemin)).getCanonicalPath()); + uc.setDefaultFolder(defaultFolder); + } + catch (NullPointerException e1) + {} + catch (IOException e2) + {} + } + else + throw new LogoError(Utils.rajoute_backslash(chemin) + " " + + Logo.messages.getString("erreur_pas_repertoire")); + + break; + case 179:// unicode + mot = getWord(param.get(0)); + if (null == mot) + throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word")); + else if (getWordLength(mot) != 1) + throw new LogoError(param.get(0) + " " + Logo.messages.getString("un_caractere")); + else + { + Interprete.operande = true; + str = String.valueOf((int) Utils.SortieTexte(itemWord(1, mot)).charAt(0)); + Interprete.calcul.push(str); + } + break; + case 180:// caractere + i = kernel.getCalculator().getInteger(param.get(0)); + if (i < 0 || i > 65535) + throw new LogoError(param.get(0) + " " + Logo.messages.getString("nombre_unicode")); + else + { + str = ""; + Interprete.operande = true; + if (i == 92) + str = "\"\\\\"; + else if (i == 10) + str = "\"\\n"; + else if (i == 32) + str = "\"\\e"; + else + { + str = String.valueOf((char) i); + try + { + Double.parseDouble(str); + } + catch (NumberFormatException e) + { + str = "\"" + str; + } + } + Interprete.calcul.push(str); + } + break; + case 181: // stoptout + cadre.error = true; + break; + case 182: // compteur + boolean erreur = false; + if (!Primitive.stackLoop.isEmpty()) + { + LoopProperties lp = Primitive.stackLoop.peek(); + if (lp.isRepeat()) + { + Interprete.operande = true; + Interprete.calcul.push(lp.getCounter().toString()); + } + else + erreur = true; + } + else + erreur = true; + if (erreur) { throw new LogoError(Logo.messages.getString("erreur_compteur")); } + break; + case 183: // controls.for repetepour + li2 = getList(param.get(1)); + li2 = new String(Utils.decoupe(li2)); + li1 = getFinalList(param.get(0)); + int nb = numberOfElements(li1); + if (nb < 3 || nb > 4) + throw new LogoError(Logo.messages.getString("erreur_repetepour")); + st = new StringTokenizer(li1); + String var = st.nextToken().toLowerCase(); + BigDecimal deb = kernel.getCalculator().numberDecimal(st.nextToken()); + BigDecimal fin = kernel.getCalculator().numberDecimal(st.nextToken()); + BigDecimal increment = BigDecimal.ONE; + if (nb == 4) + increment = kernel.getCalculator().numberDecimal(st.nextToken()); + if (var.equals("")) + throw new LogoError(Logo.messages.getString("variable_vide")); + try + { + Double.parseDouble(var); + throw new LogoError(Logo.messages.getString("erreur_nom_nombre_variable")); + } + catch (NumberFormatException e) + { + LoopFor lf = new LoopFor(deb, fin, increment, li2, var); + lf.AffecteVar(true); + + if ((increment.compareTo(BigDecimal.ZERO) == 1 && fin.compareTo(deb) >= 0) + || (increment.compareTo(BigDecimal.ZERO) == -1 && fin.compareTo(deb) <= 0)) + { + cadre.getKernel().getInstructionBuffer().insert(li2 + Primitive.END_LOOP + " "); + Primitive.stackLoop.push(lf); + } + } + break; + case 184: // absolue + Interprete.operande = true; + Interprete.calcul.push(kernel.getCalculator().abs(param.get(0))); + break; + case 185: // remplace + String reponse = ""; + liste = getFinalList(param.get(0)); + int entier = kernel.getCalculator().getInteger(param.get(1)); + mot = getWord(param.get(2)); + if (null != mot && mot.equals("")) + mot = "\\v"; + if (null == mot) + mot = "[ " + getFinalList(param.get(2)) + "]"; + char ch; + int compteur = 1; + boolean espace = true; + boolean crochet = false; + boolean error = true; + for (int j = 0; j < liste.length(); j++) + { + if (compteur == entier) + { + error = false; + compteur = j; + break; + } + ch = liste.charAt(j); + if (ch == '[') + { + if (espace) + crochet = true; + espace = false; + } + if (ch == ' ') + { + espace = true; + if (crochet) + { + crochet = false; + j = extractList(liste, j); + } + compteur++; + } + } + if (error) + throw new LogoError(Logo.messages.getString("y_a_pas") + " " + entier + " " + + Logo.messages.getString("element_dans_liste") + liste + "]"); + reponse = "[ " + liste.substring(0, compteur) + mot; + // On extrait le mot suivant + if (compteur + 1 < liste.length() && liste.charAt(compteur) == '[' + && liste.charAt(compteur + 1) == ' ') + { + compteur = extractList(liste, compteur + 2); + reponse += liste.substring(compteur) + "] "; + + } + else + { + for (i = compteur + 1; i < liste.length(); i++) + { + if (liste.charAt(i) == ' ') + { + compteur = i; + break; + } + } + reponse += liste.substring(compteur) + "] "; + } + Interprete.operande = true; + Interprete.calcul.push(reponse); + break; + case 186: // ajoute + reponse = ""; + liste = getFinalList(param.get(0)); + entier = kernel.getCalculator().getInteger(param.get(1)); + mot = getWord(param.get(2)); + if (null != mot && mot.equals("")) + mot = "\\v"; + if (null == mot) + mot = "[ " + getFinalList(param.get(2)) + "]"; + compteur = 1; + espace = true; + crochet = false; + error = true; + for (int j = 0; j < liste.length(); j++) + { + if (compteur == entier) + { + error = false; + compteur = j; + break; + } + ch = liste.charAt(j); + if (ch == '[') + { + if (espace) + crochet = true; + espace = false; + } + if (ch == ' ') + { + espace = true; + if (crochet) + { + crochet = false; + j = extractList(liste, j); + } + compteur++; + if (j == liste.length() - 1 && compteur == entier) + { + error = false; + compteur = liste.length(); + } + } + } + if (error && entier != compteur) + throw new LogoError(Logo.messages.getString("y_a_pas") + " " + entier + " " + + Logo.messages.getString("element_dans_liste") + liste + "]"); + if (!liste.trim().equals("")) + reponse = "[ " + liste.substring(0, compteur) + mot + " " + liste.substring(compteur) + "] "; + else + reponse = "[ " + mot + " ] "; + Interprete.operande = true; + Interprete.calcul.push(reponse); + break; + case 187: // gris + colorCode(8); + break; + case 188: // grisclair + colorCode(9); + break; + case 189: // rougefonce + colorCode(10); + break; + case 190: // vertfonce + colorCode(11); + break; + case 191: // bleufonce + colorCode(12); + break; + case 192: // orange + colorCode(13); + break; + case 193: // rose + colorCode(14); + break; + case 194: // violet + colorCode(15); + break; + case 195: // marron + colorCode(16); + break; + case 196: // noir + colorCode(0); + break; + case 197: // rouge + colorCode(1); + break; + case 198: // vert + colorCode(2); + break; + case 199: // jaune + colorCode(3); + break; + case 200: // bleu + colorCode(4); + break; + case 201: // magenta + colorCode(5); + break; + case 202: // cyan + colorCode(6); + break; + case 203: // blanc + colorCode(7); + break; + case 204: // Parenthese fermante + // Distinguons les deux cas : (3)*2 et (4+3)*2 + // Le 3 est ici a retourner au + + boolean a_retourner = true; + // On enleve le "(" correspondant a la parenthese ouvrante + // de la + // pile nom + // a condition que l'element attendant de la pile nom ne + // soit + // pas une procedure + boolean est_procedure = false; + int pos = Interprete.nom.lastIndexOf("("); + if (pos == -1) + { + // Parenthese fermante sans parenthese ouvrante au + // prealable + throw new LogoError(Logo.messages.getString("parenthese_ouvrante")); + } + else + { // Evitons l'erreur en cas de par exemple: "ec )" + // (parenthese fermante sans ouvrante)--> else a + // executer qu'en cas de non erreur + if (Interprete.drapeau_ouvrante) + { + // parenthese vide + throw new LogoError(Logo.messages.getString("parenthese_vide")); + + } + for (int j = pos; j < Interprete.nom.size(); j++) + { + String proc = Interprete.nom.get(j).toLowerCase(); + if (Primitive.primitives.containsKey(proc)) + est_procedure = true; + else + { + if (wp.isExecutable(proc)) // TODO changed + { + est_procedure = true; + break; + } + } + } + } + // Si une procedure est presente dans la pile nom, on garde + // les parenteses + // System.out.println(Primitive.primitives.containsKey("puissance")+" + // "+est_procedure); + if (est_procedure) + { + cadre.getKernel().getInstructionBuffer().insert(") "); + } + // Sinon on les enleve avec leurs imbrications eventuelles + else + { + if (Interprete.en_cours.isEmpty() || !Interprete.en_cours.peek().equals("(")) + { + throw new LogoError(Logo.messages.getString("parenthese_ouvrante")); + } + else + Interprete.en_cours.pop(); + if (!Interprete.nom.isEmpty()) + { + if (Interprete.nom.peek().equals("(")) + a_retourner = false; + pos = Interprete.nom.lastIndexOf("("); + if (pos == -1) + { + // Parenthese fermante sans parenthese ouvrante + // au prelable + throw new LogoError(Logo.messages.getString("parenthese_ouvrante")); + } + else + { + Interprete.nom.removeElementAt(pos); + // S'il y a imbrication de parentheses (((20))) + pos--; + instruction = cadre.getKernel().getInstructionBuffer(); + while (instruction.getNextWord().equals(")") && (pos > -1)) + { + if (!Interprete.nom.isEmpty() && Interprete.nom.get(pos).equals("(")) + { + instruction.deleteFirstWord(")"); + Interprete.nom.removeElementAt(pos); + pos--; + } + else + break; + } + } + } + } + if (Interprete.calcul.isEmpty()) + { + Interprete.operande = false; + } + else + { + Interprete.operande = true; + Interprete.drapeau_fermante = a_retourner; + } + break; + case 205: // fixestyle + boolean gras = false; + boolean italique = false; + boolean souligne = false; + boolean exposant = false; + boolean indice = false; + boolean barre = false; + mot = getWord(param.get(0)); + if (null == mot) + liste = getFinalList(param.get(0)); + else + liste = mot; + if (liste.trim().equals("")) + liste = Logo.messages.getString("style.none"); + st = new StringTokenizer(liste); + while (st.hasMoreTokens()) + { + element = st.nextToken().toLowerCase(); + if (element.equals(Logo.messages.getString("style.underline").toLowerCase())) + { + souligne = true; + } + else if (element.equals(Logo.messages.getString("style.bold").toLowerCase())) + { + gras = true; + } + else if (element.equals(Logo.messages.getString("style.italic").toLowerCase())) + { + italique = true; + } + else if (element.equals(Logo.messages.getString("style.exposant").toLowerCase())) + { + exposant = true; + } + else if (element.equals(Logo.messages.getString("style.subscript").toLowerCase())) + { + indice = true; + } + else if (element.equals(Logo.messages.getString("style.strike").toLowerCase())) + { + barre = true; + } + else if (element.equals(Logo.messages.getString("style.none").toLowerCase())) + {} + else + throw new LogoError(Logo.messages.getString("erreur_fixestyle")); + } + cadre.getHistoryPanel().getDsd().fixegras(gras); + cadre.getHistoryPanel().getDsd().fixeitalique(italique); + cadre.getHistoryPanel().getDsd().fixesouligne(souligne); + cadre.getHistoryPanel().getDsd().fixeexposant(exposant); + cadre.getHistoryPanel().getDsd().fixeindice(indice); + cadre.getHistoryPanel().getDsd().fixebarre(barre); + break; + case 206: // style + StringBuffer buffer = new StringBuffer(); + compteur = 0; + if (cadre.getHistoryPanel().getDsd().estgras()) + { + buffer.append(Logo.messages.getString("style.bold").toLowerCase() + " "); + compteur++; + } + if (cadre.getHistoryPanel().getDsd().estitalique()) + { + buffer.append(Logo.messages.getString("style.italic").toLowerCase() + " "); + compteur++; + } + if (cadre.getHistoryPanel().getDsd().estsouligne()) + { + buffer.append(Logo.messages.getString("style.underline").toLowerCase() + " "); + compteur++; + } + if (cadre.getHistoryPanel().getDsd().estexposant()) + { + buffer.append(Logo.messages.getString("style.exposant").toLowerCase() + " "); + compteur++; + } + if (cadre.getHistoryPanel().getDsd().estindice()) + { + buffer.append(Logo.messages.getString("style.subscript").toLowerCase() + " "); + compteur++; + } + if (cadre.getHistoryPanel().getDsd().estbarre()) + { + buffer.append(Logo.messages.getString("style.strike").toLowerCase() + " "); + compteur++; + } + Interprete.operande = true; + if (compteur == 0) + Interprete.calcul.push("\"" + Logo.messages.getString("style.none").toLowerCase()); + else if (compteur == 1) + Interprete.calcul.push("\"" + new String(buffer).trim()); + else if (compteur > 1) + Interprete.calcul.push("[ " + new String(buffer) + "]"); + break; + case 207: // listaillefenetre + Interprete.operande = true; + java.awt.Point p = cadre.scrollArea.getViewport().getViewPosition(); + Rectangle rec = cadre.scrollArea.getVisibleRect(); + sb = new StringBuffer(); + int x1 = p.x - w / 2; + int y1 = h / 2 - p.y; + int x2 = x1 + rec.width - cadre.scrollArea.getVerticalScrollBar().getWidth(); + int y2 = y1 - rec.height + cadre.scrollArea.getHorizontalScrollBar().getHeight(); + sb.append("[ "); + sb.append(x1); + sb.append(" "); + sb.append(y1); + sb.append(" "); + sb.append(x2); + sb.append(" "); + sb.append(y2); + sb.append(" ] "); + Interprete.calcul.push(new String(sb)); + break; + case 208: // LongueurEtiquette + mot = getWord(param.get(0)); + if (null != mot) + mot = Utils.SortieTexte(mot); + else + mot = getFinalList(param.get(0)).trim(); + Interprete.operande = true; + fm = cadre.getDrawPanel().getGraphics().getFontMetrics(cadre.getDrawPanel().getGraphicsFont()); + longueur = fm.stringWidth(mot); + Interprete.calcul.push(String.valueOf(longueur)); + break; + case 209: // envoietcp // sendtcp // enviatcp etcp + Interprete.operande = true; + mot = getWord(param.get(0)); + if (null == mot) { throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word")); } + mot = mot.toLowerCase(); + liste = ""; + liste = getFinalList(param.get(1)); + NetworkClientSend ncs = new NetworkClientSend(mot, liste); + Interprete.calcul.push("[ " + ncs.getAnswer() + " ] "); + /* + * { + * liste = "[ "; mot2 = + * getFinalList(param.get(0).toString()); liste += mot2 + " + * ]"; String rip = liste.substring(2,17); // cadre.ecris("perso + * ", rip + "\n"); + * //para debug String rdat = "_" + liste.substring(18,23) + + * "*\n\r"; // + * cadre.ecris("perso", rdat + "\n"); //para debug Socket + * echoSocket = null; + * DataOutputStream tcpout = null; BufferedReader tcpin = + * null; String resp = + * null; { echoSocket = new Socket(rip, 1948); tcpout = + * new + * DataOutputStream(echoSocket.getOutputStream()); tcpin= + * new BufferedReader(new + * InputStreamReader(echoSocket.getInputStream())); + * tcpout.writeBytes(rdat); + * resp = tcpin.readLine(); // readLine detiene el programa + * hasta que recibe una + * respuesta del robot. Que hacer si no recibe nada? + * tcpout.close(); + * tcpin.close(); echoSocket.close(); } catch + * (UnknownHostException e) { throw + * new myException( Logo.messages.getString("erreur_tcp")); + * } catch + * (IOException e) { throw new myException( + * Logo.messages.getString("erreur_tcp")); } + * Interprete.calcul.push("[ " + resp + " + * ]"); } catch(myException e){} + */ + break; + case 210: // ecoutetcp + /** + * Marko Zivkovic : new implementation of context switch. + * saving of workspace (now context) is done in {@link ContextManager} + */ + Interprete.operande = false; + new NetworkServer(cadre); + + break; + case 211: // executetcp + mot = getWord(param.get(0)); + if (null == mot) { throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word")); } + mot = mot.toLowerCase(); + liste = ""; + liste = getFinalList(param.get(1)); + new NetworkClientExecute(cadre.getKernel(), mot, liste); + break; + case 212: // \x internal operator to specify + // the end of network instructions with + // "executetcp" + // have to replace workspace + Interprete.operande = false; + + /* + * Marko Zivkovic : new implementation of network context + */ + wp.popNetworkMode(); + + break; + case 213: // chattcp + Interprete.operande = false; + mot = getWord(param.get(0)); + if (null == mot) { throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word")); } + mot = mot.toLowerCase(); + liste = ""; + liste = getFinalList(param.get(1)); + new NetworkClientChat(cadre, mot, liste); + break; + case 214: // init resetall + Interprete.operande = false; + // resize drawing zone if necessary + if (h != 1000 || w != 1000) + { + h = 1000; + w = 1000; + cadre.resizeDrawingZone(); + } + uc.setDrawGrid(false); + uc.setDrawXAxis(false); + uc.setDrawYAxis(false); + cadre.getDrawPanel().origine(); + kernel.getActiveTurtle().stroke = new BasicStroke(1); + cadre.getDrawPanel().setBackgroundColor(Color.WHITE); + if (kernel.getActiveTurtle().id == 0) + { + uc.setActiveTurtle(0); + } + DrawPanel.WINDOW_MODE = DrawPanel.WINDOW_CLASSIC; + chemin = "tortue0.png"; + kernel.change_image_tortue(chemin); + cadre.getDrawPanel().fcfg(Color.WHITE); + cadre.getDrawPanel().fcc(Color.BLACK); + cadre.getDrawPanel().setAnimation(false); + font = new Font("dialog", Font.PLAIN, 12); + WSManager.getWorkspaceConfig().setFont(font); + kernel.getActiveTurtle().police = 12; + cadre.getDrawPanel().setGraphicsFont(font); + HistoryPanel.fontPrint = UserConfig.police_id(font); + cadre.getHistoryPanel().getDsd().fixepolice(12); + cadre.getHistoryPanel().getDsd().fixenompolice(HistoryPanel.fontPrint); + cadre.getHistoryPanel().getDsd().fixecouleur(Color.black); + uc.setPenShape(PenShape.SQUARE); + uc.setQuality(DrawQuality.NORMAL); + kernel.setDrawingQuality(uc.getQuality()); + kernel.setNumberOfTurtles(16); + uc.setTurtleSpeed(0); + Kernel.mode_trace = false; + DrawPanel.WINDOW_MODE = DrawPanel.WINDOW_CLASSIC; + cadre.getDrawPanel().zoom(1, false); + break; + case 215: // tc taillecrayon + Interprete.operande = true; + double penwidth = 2 * kernel.getActiveTurtle().getPenWidth(); + Interprete.calcul.push(String.valueOf(MyCalculator.teste_fin_double(penwidth))); + break; + case 216: // setpenshape=ffc fixeformecrayon + Interprete.operande = false; + i = kernel.getCalculator().getInteger(param.get(0)); + if (i != PenShape.OVAL.getValue() && i != PenShape.SQUARE.getValue()) + { + str = Utils.primitiveName("setpenshape") + " " + Logo.messages.getString("error_bad_values"); + str += " " + PenShape.SQUARE.getValue() + " " + PenShape.OVAL.getValue(); + throw new LogoError(str); + } + uc.setPenShape(PenShape.getPenShape(i)); + cadre.getDrawPanel().updateAllTurtleShape(); + cadre.getDrawPanel().setStroke(kernel.getActiveTurtle().crayon); + break; + case 217: // penshape=fc formecrayon + Interprete.operande = true; + Interprete.calcul.push(String.valueOf(uc.getPenShape().getValue())); + break; + case 218: // setdrawingquality=fqd fixequalitedessin + Interprete.operande = false; + i = kernel.getCalculator().getInteger(param.get(0)); + if (i != DrawQuality.NORMAL.getValue() && i != DrawQuality.HIGH.getValue() + && i != DrawQuality.LOW.getValue()) + { + str = Utils.primitiveName("setdrawingquality") + " " + + Logo.messages.getString("error_bad_values") + " 0 1 2"; + throw new LogoError(str); + } + uc.setQuality(DrawQuality.getDrawQuality(i)); + kernel.setDrawingQuality(uc.getQuality()); + break; + case 219: // drawingquality=qd qualitedessin + Interprete.operande = true; + Interprete.calcul.push(String.valueOf(uc.getQuality().getValue())); + break; + case 220: // setturtlesnumber=fmt fixemaxtortues + Interprete.operande = false; + i = kernel.getCalculator().getInteger(param.get(0)); + if (i < 0) + { + String fmt = Utils.primitiveName("setturtlesnumber"); + throw new LogoError(fmt + " " + Logo.messages.getString("attend_positif")); + } + else if (i == 0) + i = 1; + kernel.setNumberOfTurtles(i); + break; + case 221: // turtlesnumber=maxtortues + Interprete.operande = true; + Interprete.calcul.push(String.valueOf(uc.getMaxTurtles())); + + break; + case 222: // setscreensize=ftd fixetailledessin + Interprete.operande = false; + + String prim = Utils.primitiveName("setscreensize"); + liste = getFinalList(param.get(0)); + int width, + height; + st = new StringTokenizer(liste); + try + { + if (!st.hasMoreTokens()) + throw new LogoError(prim + " " + Logo.messages.getString("n_aime_pas") + liste + + Logo.messages.getString("comme_parametre")); + width = Integer.parseInt(st.nextToken().toString()); + if (!st.hasMoreTokens()) + throw new LogoError(prim + " " + Logo.messages.getString("n_aime_pas") + liste + + Logo.messages.getString("comme_parametre")); + height = Integer.parseInt(st.nextToken().toString()); + } + catch (NumberFormatException e) + { + throw new LogoError(prim + " " + Logo.messages.getString("n_aime_pas") + liste + + Logo.messages.getString("comme_parametre")); + } + if (st.hasMoreTokens()) + throw new LogoError(prim + " " + Logo.messages.getString("n_aime_pas") + liste + + Logo.messages.getString("comme_parametre")); + boolean changement = false; + if (height != h) + changement = true; + int tmp_hauteur = h; + h = height; + if (width != w) + changement = true; + int tmp_largeur = w; + w = width; + if (w < 100 || h < 100) + { + w = 1000; + h = 1000; + } + if (changement) + { + int memoire_necessaire = w * h * 4 / 1024 / 1024; + int memoire_image = tmp_hauteur * tmp_largeur * 4 / 1024 / 1024; + long free = Runtime.getRuntime().freeMemory() / 1024 / 1024; + long total = Runtime.getRuntime().totalMemory() / 1024 / 1024; + /* + * System.out.println("memoire nécessaire + * "+memoire_necessaire); System.out.println("memoire + * image + * "+memoire_image); System.out.println("memoire + * libre "+free); System.out.println("memoire totale + * "+total); System.out.println("memoire envisagee + * "+(total-free+memoire_necessaire-memoire_image)); + * System.out.println(); + */ + if (total - free + memoire_necessaire - memoire_image < GlobalConfig.getMaximumMemory() * 0.8) + { + cadre.resizeDrawingZone(); + } + else + { + w = tmp_largeur; + h = tmp_hauteur; + long conseil = 64 * ((total - free + memoire_necessaire - memoire_image) / 64) + 64; + if (total - free + memoire_necessaire - memoire_image > 0.8 * conseil) + conseil += 64; + if (conseil == GlobalConfig.getMaximumMemory()) + conseil += 64; + String message = Logo.messages.getString("erreur_memoire") + " " + conseil + "\n" + + Logo.messages.getString("relancer"); + jt = new MyTextAreaDialog(message); + JOptionPane.showMessageDialog(cadre.getFrame(), jt, Logo.messages.getString("erreur"), + JOptionPane.ERROR_MESSAGE); + } + } + break; + case 223: // guibutton guibouton + String identifier = getWord(param.get(0)); + if (null == identifier) + throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word")); + mot = getWord(param.get(1)); + if (null == mot) + throw new LogoError(param.get(1) + " " + Logo.messages.getString("error.word")); + GuiButton gb = new GuiButton(identifier.toLowerCase(), mot, cadre); + cadre.getDrawPanel().addToGuiMap(gb); + break; + case 224: // guiaction + identifier = getWord(param.get(0)); + if (null == identifier) + throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word")); + liste = getFinalList(param.get(1)); + cadre.getDrawPanel().guiAction(identifier, liste); + break; + case 225: // guiremove + identifier = getWord(param.get(0)); + if (null == identifier) + throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word")); + cadre.getDrawPanel().guiRemove(identifier); + break; + case 226: // guiposition + identifier = getWord(param.get(0)); + if (null == identifier) + throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word")); + liste = getFinalList(param.get(1)); + cadre.getDrawPanel().guiposition(identifier, liste, Utils.primitiveName("guiposition")); + break; + case 227: // guidraw + identifier = getWord(param.get(0)); + if (null == identifier) + throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word")); + cadre.getDrawPanel().guiDraw(identifier); + break; + case 228: // zoom + Interprete.operande = false; + d = kernel.getCalculator().numberDouble(param.get(0)); + if (d <= 0) + { + String name = Utils.primitiveName("zoom"); + throw new LogoError(name + " " + Logo.messages.getString("attend_positif")); + } + cadre.getDrawPanel().zoom(d, false); + break; + case 229: // grille + Interprete.operande = false; + primitive2D("grille"); + int[] args = new int[2]; + for (i = 0; i < 2; i++) + { + args[i] = kernel.getCalculator().getInteger(param.get(i)); + if (args[i] < 0) + { + String grille = Utils.primitiveName("grille"); + throw new LogoError(grille + " " + Logo.messages.getString("attend_positif")); + } + else if (args[i] == 0) + { + args[i] = 1; + } + } + uc.setDrawGrid(true); + uc.setXGrid(args[0]); + uc.setYGrid(args[1]); + cadre.getDrawPanel().videecran(); + break; + case 230: // stopgrille + Interprete.operande = false; + uc.setDrawGrid(false); + cadre.getDrawPanel().videecran(); + break; + case 231: // stopanimation + cadre.getDrawPanel().setAnimation(false); + Interprete.operande = false; + break; + case 232: // stoptrace + Kernel.mode_trace = false; + Interprete.operande = false; + break; + case 233: // guimenu + identifier = getWord(param.get(0)); + if (null == identifier) + throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word")); + liste = getFinalList(param.get(1)); + GuiMenu gm = new GuiMenu(identifier.toLowerCase(), liste, cadre); + cadre.getDrawPanel().addToGuiMap(gm); + break; + case 234: // axis + + Interprete.operande = false; + + primitive2D("axis"); + i = kernel.getCalculator().getInteger(param.get(0)); + if (i < 0) + { + String name = Utils.primitiveName("axis"); + throw new LogoError(name + " " + Logo.messages.getString("attend_positif")); + } + else if (i < 25) + i = 25; + uc.setDrawXAxis(true); + uc.setXAxis(i); + uc.setDrawYAxis(true); + uc.setYAxis(i); + cadre.getDrawPanel().videecran(); + break; + case 235: // xaxis + Interprete.operande = false; + primitive2D("xaxis"); + i = kernel.getCalculator().getInteger(param.get(0)); + if (i < 0) + { + String name = Utils.primitiveName("xaxis"); + throw new LogoError(name + " " + Logo.messages.getString("attend_positif")); + } + else if (i < 25) + i = 25; + uc.setDrawXAxis(true); + uc.setXAxis(i); + cadre.getDrawPanel().videecran(); + break; + case 236: // yaxis + Interprete.operande = false; + primitive2D("yaxis"); + i = kernel.getCalculator().getInteger(param.get(0)); + if (i < 0) + { + String name = Utils.primitiveName("yaxis"); + throw new LogoError(name + " " + Logo.messages.getString("attend_positif")); + } + else if (i < 25) + i = 25; + uc.setDrawYAxis(true); + uc.setYAxis(i); + cadre.getDrawPanel().videecran(); + break; + case 237: // stopaxis + uc.setDrawXAxis(false); + uc.setDrawYAxis(false); + Interprete.operande = false; + cadre.getDrawPanel().videecran(); + break; + case 238: // bye + cadre.closeWindow(); + break; + case 239: // var? variable? + Interprete.operande = true; + mot = getWord(param.get(0)); + if (null == mot) + throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word")); + mot = mot.toLowerCase(); + if (wp.getGlobals().getVariables().contains(mot) || Interprete.locale.containsKey(mot)) // TODO + // changed + Interprete.calcul.push(Logo.messages.getString("vrai")); + else + Interprete.calcul.push(Logo.messages.getString("faux")); + break; + case 240: // axiscolor= couleuraxes + Interprete.operande = true; + c = new Color(uc.getAxisColor()); + Interprete.calcul.push("[ " + c.getRed() + " " + c.getGreen() + " " + c.getBlue() + " ] "); + + break; + case 241: // gridcolor=couleurgrille + Interprete.operande = true; + c = new Color(uc.getGridColor()); + Interprete.calcul.push("[ " + c.getRed() + " " + c.getGreen() + " " + c.getBlue() + " ] "); + break; + case 242: // grid?=grille? + Interprete.operande = true; + if (uc.isDrawGrid()) + Interprete.calcul.push(Logo.messages.getString("vrai")); + else + Interprete.calcul.push(Logo.messages.getString("faux")); + break; + case 243: // xaxis?=axex? + Interprete.operande = true; + if (uc.isDrawXAxis()) + Interprete.calcul.push(Logo.messages.getString("vrai")); + else + Interprete.calcul.push(Logo.messages.getString("faux")); + break; + case 244: // yaxis?=axey? + Interprete.operande = true; + if (uc.isDrawYAxis()) + Interprete.calcul.push(Logo.messages.getString("vrai")); + else + Interprete.calcul.push(Logo.messages.getString("faux")); + break; + case 245: // setgridcolor=fcg fixecouleurgrille + Interprete.operande = false; + if (isList(param.get(0))) + { + uc.setGridColor(rgb(param.get(0), Utils.primitiveName("setgridcolor")).getRGB()); + } + else + { + int coul = kernel.getCalculator().getInteger(param.get(0)) % DrawPanel.defaultColors.length; + if (coul < 0) + coul += DrawPanel.defaultColors.length; + uc.setGridColor(DrawPanel.defaultColors[coul].getRGB()); + } + break; + case 246: // setaxiscolor=fca fixecouleuraxes + Interprete.operande = false; + if (isList(param.get(0))) + { + uc.setAxisColor(rgb(param.get(0), Utils.primitiveName("setaxiscolor")).getRGB()); + } + else + { + int coul = kernel.getCalculator().getInteger(param.get(0)) % DrawPanel.defaultColors.length; + if (coul < 0) + coul += DrawPanel.defaultColors.length; + uc.setAxisColor(DrawPanel.defaultColors[coul].getRGB()); + } + break; + case 247: // perspective + + cadre.getDrawPanel().perspective(); + + break; + case 248:// rightroll=rd roulisdroite + delay(); + primitive3D("3d.rightroll"); + cadre.getDrawPanel().rightroll(kernel.getCalculator().numberDouble(param.pop())); + break; + case 249:// uppitch=cabre + delay(); + primitive3D("3d.uppitch"); + cadre.getDrawPanel().uppitch(kernel.getCalculator().numberDouble(param.pop())); + break; + case 250:// leftroll=rg roulisgauche + delay(); + primitive3D("3d.leftroll"); + cadre.getDrawPanel().rightroll(-kernel.getCalculator().numberDouble(param.pop())); + break; + case 251:// downpitch=pique + delay(); + primitive3D("3d.downpitch"); + cadre.getDrawPanel().uppitch(-kernel.getCalculator().numberDouble(param.pop())); + break; + case 252:// roll=roulis + primitive3D("3d.roll"); + Interprete.operande = true; + Interprete.calcul.push(MyCalculator.teste_fin_double(kernel.getActiveTurtle().roll)); + break; + case 253:// pitch=cabrement tangage + primitive3D("3d.pitch"); + Interprete.operande = true; + Interprete.calcul.push(MyCalculator.teste_fin_double(kernel.getActiveTurtle().pitch)); + break; + case 254:// setroll=fixeroulis + primitive3D("3d.setroll"); + delay(); + cadre.getDrawPanel().setRoll(kernel.getCalculator().numberDouble(param.pop())); + break; + case 255:// setpitch=fixetangage + primitive3D("3d.setpitch"); + delay(); + cadre.getDrawPanel().setPitch(kernel.getCalculator().numberDouble(param.pop())); + break; + case 256:// setorientation=fixeorientation + primitive3D("3d.setorientation"); + delay(); + cadre.getDrawPanel().setOrientation(getFinalList(param.pop())); + break; + case 257: // orientation=orientation + primitive3D("3d.orientation"); + Interprete.operande = true; + String pitch = MyCalculator.teste_fin_double(kernel.getActiveTurtle().pitch); + String roll = MyCalculator.teste_fin_double(kernel.getActiveTurtle().roll); + String heading = MyCalculator.teste_fin_double(kernel.getActiveTurtle().heading); + Interprete.calcul.push("[ " + roll + " " + pitch + " " + heading + " ] "); + break; + case 258: // setxyz=fposxyz + primitive3D("3d.setxyz"); + cadre.getDrawPanel().fpos( + kernel.getCalculator().numberDouble(param.get(0)) + " " + + kernel.getCalculator().numberDouble(param.get(1)) + " " + + kernel.getCalculator().numberDouble(param.get(2))); + break; + case 259: // setz=fixez + delay(); + primitive3D("3d.setz"); + cadre.getDrawPanel().fpos( + kernel.getActiveTurtle().X + " " + kernel.getActiveTurtle().Y + " " + + kernel.getCalculator().numberDouble(param.get(0))); + break; + case 260: // pprop=dprop + Interprete.operande = false; + mot = getWord(param.get(0)); + if (null == mot) + throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word")); + mot2 = getWord(param.get(1)); + if (null == mot2) + throw new LogoError(param.get(1) + " " + Logo.messages.getString("error.word")); + wp.getPropertyLists().addPropList(mot, mot2, param.get(2)); // TODO + // changed + break; + case 261: // gprop=rprop + Interprete.operande = true; + mot = getWord(param.get(0)); + if (null == mot) + throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word")); + mot2 = getWord(param.get(1)); + if (null == mot2) + throw new LogoError(param.get(1) + " " + Logo.messages.getString("error.word")); + value = wp.getPropertyLists().getPropList(mot, mot2); // TODO + // CHANGED + if (value.startsWith("[")) + value += " "; + Interprete.calcul.push(value); + break; + case 262: // remprop=efprop + Interprete.operande = false; + mot = getWord(param.get(0)); + if (null == mot) + throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word")); + mot2 = getWord(param.get(1)); + if (null == mot2) + throw new LogoError(param.get(1) + " " + Logo.messages.getString("error.word")); + wp.getPropertyLists().removePropList(mot, mot2); // TODO + // CHANGED + break; + case 263: // plist=lprop + Interprete.operande = true; + mot = getWord(param.get(0)); + if (null == mot) + throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word")); + Interprete.calcul.push(wp.getPropertyLists().displayPropList(mot)); // TODO + // CHANGED + + break; + case 264: // polystart=polydef + DrawPanel.record3D = DrawPanel.record3D_POLYGON; + cadre.initViewer3D(); + // if (null==DrawPanel.listPoly) DrawPanel.listPoly=new + // java.util.Vector<Shape3D>(); + DrawPanel.poly = new ElementPolygon(cadre.getViewer3D()); + break; + case 265: // polyend=polyfin + DrawPanel.record3D = DrawPanel.record3D_NONE; + DrawPanel.poly.addToScene(); + break; + case 266: // polyview=polyaf vue3d + primitive3D("3d.polyview"); + cadre.viewerOpen(); + break; + case 267: // linestart=lignedef + DrawPanel.record3D = DrawPanel.record3D_LINE; + cadre.initViewer3D(); + // if (null==DrawPanel.listPoly) DrawPanel.listPoly=new + // java.util.Vector<Shape3D>(); + DrawPanel.poly = new ElementLine(cadre.getViewer3D(), cadre.getKernel().getActiveTurtle().getPenWidth()); + DrawPanel.poly.addVertex(new Point3d(kernel.getActiveTurtle().X / 1000, + kernel.getActiveTurtle().Y / 1000, kernel.getActiveTurtle().Z / 1000), kernel + .getActiveTurtle().couleurcrayon); + break; + case 268: // lineend=lignefin + DrawPanel.record3D = DrawPanel.record3D_NONE; + DrawPanel.poly.addToScene(); + break; + case 269: // pointstart=pointdef + DrawPanel.record3D = DrawPanel.record3D_POINT; + cadre.initViewer3D(); + // if (null==DrawPanel.listPoly) DrawPanel.listPoly=new + // java.util.Vector<Shape3D>(); + DrawPanel.poly = new ElementPoint(cadre.getViewer3D(), cadre.getKernel().getActiveTurtle().getPenWidth()); + break; + case 270: // pointend=pointfin + DrawPanel.record3D = DrawPanel.record3D_NONE; + DrawPanel.poly.addToScene(); + break; + case 271: // textstart=textedef + DrawPanel.record3D = DrawPanel.record3D_TEXT; + cadre.initViewer3D(); + // if (null==DrawPanel.listText) DrawPanel.listText=new + // java.util.Vector<TransformGroup>(); + DrawPanel.poly = null; + break; + case 272: // textend=textefin + DrawPanel.record3D = DrawPanel.record3D_NONE; + break; + case 273: // operator <= + infequal(param); + break; + case 274: // operator >= + supequal(param); + break; + case 275: // primitives + Interprete.operande = true; + Interprete.calcul.push(kernel.primitive.getAllPrimitives()); + break; + case 276: // listesproprietes propertylists + Interprete.operande = true; + Interprete.calcul.push(new String(getAllpropertyLists())); + break; + case 277: // contenu + Interprete.operande = true; + sb = new StringBuffer("[ "); + sb.append(this.getAllProcedures()); + sb.append(this.getAllVariables()); + sb.append(this.getAllpropertyLists()); + sb.append("] "); + Interprete.calcul.push(new String(sb)); + break; + case 278: // erpl=eflp effacelistepropriete + Interprete.operande = false; + this.erase(param.get(0), "propertylist"); + break; + case 279: // arithmetic.exp + Interprete.operande = true; + Interprete.calcul.push(kernel.getCalculator().exp(param.get(0))); + break; + case 280: // arithmetic.log + Interprete.operande = true; + Interprete.calcul.push(kernel.getCalculator().log(param.get(0))); + break; + case 281: // controls.ifelse + liste = getList(param.get(1)); + liste = new String(Utils.decoupe(liste)); + pred = predicat(param.get(0)); + liste2 = getList(param.get(2)); + liste = new String(Utils.decoupe(liste)); + kernel.primitive.si(pred, liste, liste2); + Interprete.renvoi_instruction = true; + break; + case 282: // workspace.ed + + /* + * Marko Zivkovic: + * With the new Workspace (UserSpace + LogoFile) ed makes + * not so much sense, because we work with + * multiple files at the same time. + * I therefore restrict this command to allow only a single + * word as parameter, to open the file, + * where the specified procedure is defined, and scroll to + * the procedure's line. + */ + + mot = this.getWord(param.get(0)); + if (mot == null) + throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word")); + if (!wp.isExecutable(mot)) + throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.proc.does.not.exist")); + + cadre.displayProcedure(mot); + break; + case 283: // workspace.edall + /* + * Marko Zivkovic: + * Again, it makes no sense to open all files at a time. + * Instead, I just open the last edited file. + */ + wp.editAll(); + break; + case 284: // controls.foreach pourchaque + // Variable name + var = getWord(param.get(0)); + // If it isn't a word + if (null == var) + throw new LogoError(param.get(0).toString() + " " + Logo.messages.getString("error.word")); + // If it's a number + else + { + try + { + Double.parseDouble(var); + throw new LogoError(Logo.messages.getString("erreur_nom_nombre_variable")); + } + catch (NumberFormatException e1) + {} + } + li2 = getList(param.get(2)); + li2 = new String(Utils.decoupe(li2)); + li1 = getWord(param.get(1)); + boolean isList = false; + if (null == li1) + { + isList = true; + li1 = getFinalList(param.get(1)); + } + Vector<String> elements = new Vector<String>(); + while (!li1.equals("")) + { + String character = ""; + // If it's a list + if (isList) + { + character = this.item(li1, 1); + // If it's a number + try + { + // Fix Bug: foreach "i [1 2 3][pr :i] + // character=1 , 2 , 3 (without quote) + Double.parseDouble(character); + li1 = li1.substring(character.length() + 1); + } + catch (NumberFormatException e) + { + // Fix Bug: foreach "i [r s t][pr :i] + // character="r , "s or "t + li1 = li1.substring(character.length()); + } + } + // If it's a word + else + { + character = this.itemWord(1, li1); + li1 = li1.substring(character.length()); + // If it isn't a number, adding a quote + try + { + Double.parseDouble(character); + } + catch (NumberFormatException e) + { + character = "\"" + character; + } + } + + elements.add(character); + } + if (elements.size() > 0) + { + LoopForEach lfe = new LoopForEach(BigDecimal.ZERO, new BigDecimal(elements.size() - 1), + BigDecimal.ONE, li2, var.toLowerCase(), elements); + lfe.AffecteVar(true); + cadre.getKernel().getInstructionBuffer().insert(li2 + Primitive.END_LOOP + " "); + Primitive.stackLoop.push(lfe); + } + break; + case 285: // controls.forever repetetoujours + li2 = getList(param.get(0)); + li2 = new String(Utils.decoupe(li2)); + LoopProperties lp = new LoopProperties(BigDecimal.ONE, BigDecimal.ZERO, BigDecimal.ONE, li2); + cadre.getKernel().getInstructionBuffer().insert(li2 + Primitive.END_LOOP + " "); + Primitive.stackLoop.push(lp); + break; + case 286: // arithmetic.setdigits + Interprete.operande = false; + kernel.initCalculator(kernel.getCalculator().getInteger(param.get(0))); + break; + case 287: // arithmetic.digits + Interprete.operande = true; + Interprete.calcul.push(String.valueOf(kernel.getCalculator().getDigits())); + break; + case 288: // workspace.text + var = getWord(param.get(0)); + if (null == var) + throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word")); + + Procedure proc = wp.getExecutable(var); + if (proc != null) + { + sb = new StringBuffer(); + sb.append("[ [ "); + // Append variable names + for (int j = 0; j < proc.nbparametre; j++) + { + sb.append(proc.variable.get(j)); + sb.append(" "); + } + for (int j = 0; j < proc.optVariables.size(); j++) + { + sb.append("[ "); + sb.append(proc.optVariables.get(j)); + sb.append(" "); + sb.append(proc.optVariablesExp.get(j).toString()); + sb.append(" ] "); + } + sb.append("] "); + // Append body procedure + sb.append(proc.cutInList()); + sb.append("] "); + Interprete.operande = true; + Interprete.calcul.push(sb.toString()); + } + else + throw new LogoError(var + " " + Logo.messages.getString("error.procedure.must.be")); + break; + case 289: // workspace.externalcommand + Interprete.operande = false; + + { + list = getFinalList(param.get(0)); + int index = numberOfElements(list); + String[] cmd = new String[index]; + for (i = 0; i < index; i++) + { + String liste1 = item(list, i + 1); + cmd[i] = Utils.SortieTexte(getFinalList(liste1).trim()); + } + try + { + /* + * String com=""; + * for(int i=0;i<cmd.length;i++){ + * com+=cmd[i]+" "; + * } + * System.out.println(com); + */ + Runtime.getRuntime().exec(cmd); + } + catch (IOException e2) + { + // System.out.println("a"); + } + + } + + { + // System.out.println("coucou"); + } + break; + case 290: // drawing.saveimage + String word = getWord(param.get(0)); + if (null == word) + throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word")); + if (word.equals("")) + throw new LogoError(param.get(0) + " " + Logo.messages.getString("mot_vide")); + // xmin, ymin, width, height + int[] coord = new int[4]; + list = getFinalList(param.get(1)); + st = new StringTokenizer(list); + if (st.countTokens() == 4) + { + try + { + int j = 0; + while (st.hasMoreTokens()) + { + coord[j] = Integer.parseInt(st.nextToken()); + j++; + } + coord[0] += w / 2; + coord[2] += w / 2; + coord[1] = h / 2 - coord[1]; + coord[3] = h / 2 - coord[3]; + if (coord[2] < coord[0]) + { + int tmp = coord[0]; + coord[0] = coord[2]; + coord[2] = tmp; + } + if (coord[3] < coord[1]) + { + int tmp = coord[1]; + coord[1] = coord[3]; + coord[3] = tmp; + } + coord[2] = coord[2] - coord[0]; + coord[3] = coord[3] - coord[1]; + } + catch (NumberFormatException e) + { + coord[0] = 0; + coord[2] = w; + coord[1] = 0; + coord[3] = h; + } + } + else + { + coord[0] = 0; + coord[2] = w; + coord[1] = 0; + coord[3] = h; + } + if (coord[2] == 0 || coord[3] == 0) + { + coord[0] = 0; + coord[2] = w; + coord[1] = 0; + coord[3] = h; + } + cadre.getDrawPanel().saveImage(word, coord); + Interprete.operande = false; + break; + case 291: // sound.mp3play + Interprete.operande = false; + if (kernel.getMp3Player() != null) + kernel.getMp3Player().getPlayer().close(); + mot = getWord(param.get(0)); + if (null == mot) + throw new LogoError(mot + " " + Logo.messages.getString("error.word")); + MP3Player player = new MP3Player(cadre, mot); + kernel.setMp3Player(player); + kernel.getMp3Player().start(); + break; + case 292: // sound.mp3stop + Interprete.operande = false; + if (null != kernel.getMp3Player()) + kernel.getMp3Player().getPlayer().close(); + break; + case 293: // zoom + Interprete.operande = true; + Interprete.calcul.push(MyCalculator.teste_fin_double(DrawPanel.zoom)); + break; + case 294: // drawing.x + Interprete.operande = true; + Interprete.calcul.push(MyCalculator.teste_fin_double(kernel.getActiveTurtle().getX())); + break; + case 295:// drawing.y + Interprete.operande = true; + Interprete.calcul.push(MyCalculator.teste_fin_double(kernel.getActiveTurtle().getY())); + break; + case 296: // drawing.z + Interprete.operande = true; + primitive3D("drawing.z"); + Interprete.calcul.push(MyCalculator.teste_fin_double(kernel.getActiveTurtle().Z)); + break; + case 297: // drawing.fillpolygon + Interprete.operande = false; + list = getFinalList(param.get(0)); + LoopFillPolygon lfp = new LoopFillPolygon(); + Primitive.stackLoop.push(lfp); + cadre.getKernel().getInstructionBuffer().insert(Utils.decoupe(list) + Primitive.END_LOOP + " "); + cadre.getDrawPanel().startRecord2DPolygon(); + break; + case 298: // arithmetic.alea + Interprete.operande = true; + Interprete.calcul.push(MyCalculator.teste_fin_double(Math.random())); + break; + case 299: // loop.dountil + li1 = getList(param.get(0)); + li1 = new String(Utils.decoupe(li1)); + li2 = getList(param.get(1)); + li2 = new String(Utils.decoupe(li2)); + instr = "\\siwhile " + Utils.primitiveName("non") + " " + li2 + "[ " + li1 + "] "; + LoopWhile lw = new LoopWhile(BigDecimal.ONE, BigDecimal.ZERO, BigDecimal.ONE, instr); + Primitive.stackLoop.push(lw); + cadre.getKernel().getInstructionBuffer().insert(instr + Primitive.END_LOOP + " "); + break; + case 300: // loop.dowhile + li1 = getList(param.get(0)); + li1 = new String(Utils.decoupe(li1)); + li2 = getList(param.get(1)); + li2 = new String(Utils.decoupe(li2)); + instr = "\\siwhile " + li2 + "[ " + li1 + "] "; + lw = new LoopWhile(BigDecimal.ONE, BigDecimal.ZERO, BigDecimal.ONE, instr); + Primitive.stackLoop.push(lw); + cadre.getKernel().getInstructionBuffer().insert(li1 + instr + Primitive.END_LOOP + " "); + break; + case 301: // arithmetic.modulo + Interprete.operande = true; + Interprete.calcul.push(kernel.getCalculator().modulo(param.get(0), param.get(1))); + break; + case 302: // drawing.setfontjustify + Interprete.operande = false; + li1 = getFinalList(param.get(0)); + kernel.getActiveTurtle().setFontJustify(li1); + break; + case 303: // drawing.fontjustify + Interprete.operande = true; + Interprete.calcul.push(kernel.getActiveTurtle().getFontJustify()); + break; + case 304: // arithmetic.inf + inf(param); + break; + case 305: // arithmetic.sup + sup(param); + break; + case 306: // arithmetic.infequal + infequal(param); + break; + case 307: // arithmetic.supequal + supequal(param); + break; + + } + } + } + + /** + * This implementation of the Logo command "define" is slightly changed towards the original XLogo + * implementation. + * In {@link xlogo.gui.Editor#analyseprocedure()} (which is now {@link LogoFile#setText(String)}), + * I added the line statement {@code wp.deleteAllProcedures()} + * because we don't want the procedures to + * be ordered by definition time in the editor. + * We want the programmer to have the procedures in the editor exactly in + * the same order as the programmer writes them. + * + * @param params + * @author Marko Zivkovic + * @throws LogoError + */ + private void define(Stack<String> params) throws LogoError + { + String procedureName; + String procedureText = null; + procedureName = getWord(params.get(0)); + if (null == procedureName) + throw new LogoError(params.get(0) + " " + Logo.messages.getString("error.word")); + if (procedureName.equals("")) + throw new LogoError(Logo.messages.getString("procedure_vide")); + String list = getFinalList(params.get(1)); + StringBuffer sb = new StringBuffer(); + for (int i = 1; i <= numberOfElements(list); i++) + { + String liste1 = item(list, i); + liste1 = getFinalList(liste1); + + // First line + if (i == 1) + { + StringTokenizer st = new StringTokenizer(liste1); + sb.append(Logo.messages.getString("pour")); + sb.append(" "); + sb.append(procedureName); + sb.append(" "); + + while (st.hasMoreTokens()) + { + // Optional variables + String token = st.nextToken(); + if (token.equals("[")) + { + sb.append("[ :"); + while (st.hasMoreTokens()) + { + token = st.nextToken(); + if (token.equals("]")) + { + sb.append("] "); + break; + } + else + { + sb.append(token); + sb.append(" "); + } + } + } + else + { + sb.append(":"); + sb.append(token); + sb.append(" "); + } + } + } + // Body of the procedure + else if (i > 1) + { + sb.append("\n"); + sb.append(liste1); + } + } + sb.append("\n"); + sb.append(Logo.messages.getString("fin")); + + /* + * Marko Zivkovic: + * In XLogo4Schools, procedures are not created by setting the text + * of the editor, + * we directly define them in LogoFiles. + * Because we have multiple files, the defined procedure will be put + * in a special file for programatically defined procedures. + * If the procedure name was already defined, then it will be + * redefined, as before in XLogo. + * If there is an ambiguity with the procedure name, an error will + * be displayed "I don't know which one to redefine..." + */ + + if (wp.isProcedureAmbiguous(procedureName)) + throw new LogoError(procedureName + " " + Logo.messages.getString("ws.redefine.ambiguous.procedure")); + // procedureText is generated above => it is executable + procedureText = "\n" + new String(sb); + Procedure procedure = new Procedure(procedureText); + + Procedure other = wp.getExecutable(procedureName); + String fileName = other != null ? other.getOwnerName() : Logo.messages.getString("ws.generated.procedure"); + procedure.setOwnerName(fileName); + + try + { + wp.defineProcedure(procedure); + } + catch (IOException e) + { + throw new LogoError(Logo.messages.getString("ws.could.not.create.file") + " " + fileName); + } + } + + /** + * This method tests if the primitive name exist in 2D mode + * + * @param name + * The primitive name + * @throws LogoError + */ + private void primitive2D(String name) throws LogoError + { + if (DrawPanel.WINDOW_MODE == DrawPanel.WINDOW_3D) + throw new LogoError(Utils.primitiveName(name) + " " + Logo.messages.getString("error.primitive2D")); + } + + /** + * This method tests if the primitive name exist in 2D mode + * + * @param name + * The primitive name + * @throws LogoError + */ + private void primitive3D(String name) throws LogoError + { + if (DrawPanel.WINDOW_MODE != DrawPanel.WINDOW_3D) + throw new LogoError(Utils.primitiveName(name) + " " + Logo.messages.getString("error.primitive3D")); + } + + /** + * Returns the code [r g b] for the color i + * + * @param i + * Integer representing the Color + */ + private void colorCode(int i) + { + Interprete.operande = true; + Color co = DrawPanel.defaultColors[i]; + Interprete.calcul.push("[ " + co.getRed() + " " + co.getGreen() + " " + co.getBlue() + " ] "); + } + + /** + * Save all procedures whose name are contained in the Stack pile + * + * @param fichier + * The patch to the saved file + * @param pile + * Stack Stack containing all procedure names + */ + private void saveProcedures(String fichier, Stack<String> pile) + { + try + { + String aecrire = ""; + boolean bool = true; + if (!fichier.endsWith(".lgo")) + fichier += ".lgo"; + String path = Utils.SortieTexte(WSManager.getUserConfig().getDefaultFolder()) + File.separator + fichier; + try + { + // TODO CHANGED + for (Procedure procedure : wp.getExecutables()) + { + if (null == pile) + bool = true; + else + bool = (pile.search(procedure.name) != -1); + if (bool) + { + aecrire += Logo.messages.getString("pour") + " " + procedure.name; + for (int j = 0; j < procedure.nbparametre; j++) + { + aecrire += " :" + procedure.variable.get(j); + } + aecrire += "\n" + procedure.instruction + Logo.messages.getString("fin") + "\n\n"; + } + } + } + catch (NullPointerException ex) + {} // Si aucune procédure n'a été définie. + Utils.writeLogoFile(path, aecrire); + } + catch (IOException e2) + { + HistoryMessenger.getInstance().dispatchError(Logo.messages.getString("error.ioecriture")); + } + } + + /** + * Returns the Image defined by the path "chemin" + * + * @param chemin + * The absolute path for the image + * @return BufferedImage defined by the path "chemin" + * @throws LogoError + * If Image format isn't valid(jpg or png) + */ + private BufferedImage getImage(String path) throws LogoError + { + BufferedImage image = null; + String pathWord = getWord(path); + if (null == pathWord) + throw new LogoError(path + " " + Logo.messages.getString("error.word")); + if (!(pathWord.endsWith(".png") || pathWord.endsWith(".jpg"))) + throw new LogoError(Logo.messages.getString("erreur_format_image")); + else + { + try + { + pathWord = Utils.SortieTexte(pathWord); + File f = new File(Utils.SortieTexte(WSManager.getUserConfig().getDefaultFolder()) + File.separator + + pathWord); + image = ImageIO.read(f); + } + catch (Exception e1) + { + throw new LogoError(Logo.messages.getString("error.iolecture")); + } + } + return image; + } + + /** + * Create a local variable called "mot" with no value. + * + * @param mot + * Variable name + */ + private void createLocaleName(String mot) + { + mot = mot.toLowerCase(); + if (!Interprete.locale.containsKey(mot)) + { + Interprete.locale.put(mot, null); + } + } + + /** + * Create a new local variable + * + * @param param + * The variable name or a list of variable names + * @throws LogoError + * If "param" isn't a list containing all variable names, or a + * word + */ + + private void locale(Stack<String> param) throws LogoError + { + String li = param.get(0); + if (LaunchPrimitive.isList(li)) + { + li = getFinalList(li); + StringTokenizer st = new StringTokenizer(li); + while (st.hasMoreTokens()) + { + String item = st.nextToken(); + isVariableName(item); + createLocaleName(item); + } + } + else + { + String mot = getWord(param.get(0)); + if (null != mot) + { + createLocaleName(mot); + } + else + throw new LogoError(param.get(0) + Logo.messages.getString("error.word")); + } + } + + /** + * returns the color defined by [r g b] contained in "ob" + * + * @param obj + * the list [r g b] + * @param name + * The name of the calling primitive + * @return The Object Color + * @throws LogoError + * If the list doesn't contain 3 numbers + */ + + private Color rgb(String obj, String name) throws LogoError + { + String liste = getFinalList(obj); + StringTokenizer st = new StringTokenizer(liste); + if (st.countTokens() != 3) + throw new LogoError(name + " " + Logo.messages.getString("color_3_arguments")); + int[] entier = new int[3]; + for (int i = 0; i < 3; i++) + { + String element = st.nextToken(); + try + { + entier[i] = (int) (Double.parseDouble(element) + 0.5); + } + catch (NumberFormatException e) + { + throw new LogoError(element + " " + Logo.messages.getString("pas_nombre")); + } + if (entier[i] < 0) + entier[i] = 0; + if (entier[i] > 255) + entier[i] = 255; + + } + return (new Color(entier[0], entier[1], entier[2])); + } + + /** + * Primitive member or member? + * + * @param param + * Stack that contains arguments for the primitive member + * @param id + * 69 --> member? or 70--> member + * @throws LogoError + * Incorrect arguments + */ + private void membre(Stack<String> param, int id) throws LogoError + { + Interprete.operande = true; + String mot_retourne = null; + boolean b = false; + String mot = getWord(param.get(1)); + String liste = "[ "; + if (null == mot) + { // on travaille sur une liste + + liste = getFinalList(param.get(1)); + StringTokenizer st = new StringTokenizer(liste); + liste = "[ "; + mot = getWord(param.get(0)); + String str; + if (null != mot && mot.equals("")) + mot = "\\v"; + if (null == mot) + mot = param.get(0).trim(); + while (st.hasMoreTokens()) + { + str = st.nextToken(); + if (str.equals("[")) + str = extractList(st); + if (!liste.equals("[ ")) + liste += str + " "; + if (str.equals(mot) && liste.equals("[ ")) + { + if (id == 69) + { + b = true; + break; + } + else + liste += str + " "; + } + } + } + else + { // on travaille sur un mot + String mot2 = getWord(param.get(0)); + if (null != mot2) + { + boolean backslash = false; + for (int i = 0; i < mot.length(); i++) + { + char c = mot.charAt(i); + if (!backslash && c == '\\') + backslash = true; + else + { + String tmp = Character.toString(c); + if (backslash) + tmp = "\\" + tmp; + if (tmp.equals(mot2)) + { + if (id == 69) + { + b = true; + break; + } + else + { + if (!backslash) + mot_retourne = mot.substring(i, mot.length()); + else + mot_retourne = mot.substring(i - 1, mot.length()); + break; + } + } + backslash = false; + } + } + } + } + if (!liste.equals("[ ")) + Interprete.calcul.push(liste + "] "); + else if (null != mot_retourne) + { + try + { + Double.parseDouble(mot_retourne); + Interprete.calcul.push(mot_retourne); + } + catch (NumberFormatException e) + { + Interprete.calcul.push(debut_chaine + mot_retourne); + } + } + else if (b) + Interprete.calcul.push(Logo.messages.getString("vrai")); + else + Interprete.calcul.push(Logo.messages.getString("faux")); + } + + /** + * Primitive before? + * + * @param param + * Stack that contains all arguments + * @throws LogoError + * Bad argument type + */ + + private void precede(Stack<String> param) throws LogoError + { + Interprete.operande = true; + boolean b = false; + String ope[] = { "", "" }; + String mot = ""; + for (int i = 0; i < 2; i++) + { + mot = getWord(param.get(i)); + if (null == mot) + throw new LogoError(param.get(i) + " " + Logo.messages.getString("pas_mot")); + else + ope[i] = mot; + } + if (ope[1].compareTo(ope[0]) > 0) + b = true; + if (b) + mot = Logo.messages.getString("vrai"); + else + mot = Logo.messages.getString("faux"); + Interprete.calcul.push(mot); + } + + private void infequal(Stack<String> param) throws LogoError + { + Interprete.operande = true; + Interprete.calcul.push(kernel.getCalculator().infequal(param)); + } + + private void supequal(Stack<String> param) throws LogoError + { + Interprete.operande = true; + Interprete.calcul.push(kernel.getCalculator().supequal(param)); + } + + private void inf(Stack<String> param) throws LogoError + { + Interprete.operande = true; + Interprete.calcul.push(kernel.getCalculator().inf(param)); + } + + private void sup(Stack<String> param) throws LogoError + { + Interprete.operande = true; + Interprete.calcul.push(kernel.getCalculator().sup(param)); + } + + /** + * / Primitive equal? + * + * @param param + * Stack that contains all arguments + * @throws LogoError + */ + private void equal(Stack<String> param) throws LogoError + { + try + { + Double.parseDouble(param.get(0)); + Double.parseDouble(param.get(1)); + Interprete.calcul.push(kernel.getCalculator().equal(param)); + } + catch (NumberFormatException e) + { + if (param.get(0).toString().equals(param.get(1).toString())) + Interprete.calcul.push(Logo.messages.getString("vrai")); + else + Interprete.calcul.push(Logo.messages.getString("faux")); + } + Interprete.operande = true; + } + + /** + * this method returns the boolean corresponding to the string st + * + * @param st + * true or false + * @return The boolean corresponding to the string st + * @throws LogoError + * If st isn't equal to true or false + */ + + private boolean predicat(String st) throws LogoError + { + if (st.toLowerCase().equals(Logo.messages.getString("vrai"))) + return true; + else if (st.toLowerCase().equals(Logo.messages.getString("faux"))) + return false; + else + throw new LogoError(st + " " + Logo.messages.getString("pas_predicat")); + + } + + /** + * Returns the word contained in st. If it isn't a word, returns null + * + * @param st + * The Object to convert + * @return The word corresponding to st + */ + private String getWord(Object st) + { // Si c'est un mot + String liste = st.toString(); + if (liste.equals("\"")) + { + debut_chaine = ""; + return ""; + } + if (liste.length() > 0 && liste.substring(0, 1).equals("\"")) + { + debut_chaine = "\""; + return (liste.substring(1, liste.length())); + } + else + try + { + if (liste == String.valueOf(Double.parseDouble(liste))) + debut_chaine = ""; + else + debut_chaine = "\""; + return Utils.SortieTexte(liste); + } + catch (NumberFormatException e) + {} + return (null); + } + + /** + * Returns the list contained in the string li without any lineNumber + * + * @param li + * The String corresponding to the list + * @return A list without any line Number tag (\0, \1, \2 ...) + * @throws LogoError + * List bad format + */ + + private String getFinalList(String li) throws LogoError + { + // remove line number + li = li.replaceAll("\\\\l([0-9])+ ", ""); + // return list + return getList(li); + } + + /** + * Returns the list contained in the string li + * + * @param li + * The String corresponding to the list + * @return A list with line Number tag (\0, \1, \2 ...) + * @throws LogoError + * List bad format + */ + private String getList(String li) throws LogoError + { + li = li.trim(); + // Retourne la liste sans crochets; + if (li.substring(0, 1).equals("[") && li.substring(li.length() - 1, li.length()).equals("]")) + { + li = li.substring(1, li.length() - 1).trim() + " "; + if (!li.equals(" ")) + return li; + else + return (""); + } + else + throw new LogoError(li + " " + Logo.messages.getString("pas_liste")); + } + + /** + * Tests if "li" is a list + * + * @param li + * The String to test + * @return true if it is a list, else false + */ + // + protected static boolean isList(String li) + { + li = li.trim(); + if (li.length() > 0 && li.substring(0, 1).equals("[") && li.substring(li.length() - 1, li.length()).equals("]")) + return (true); + return false; + } + + // Format the List (only one space between two elements) + private String formatList(String li) + { + String tampon = ""; + String precedent = ""; + StringTokenizer st = new StringTokenizer(li, " []", true); + String element = ""; + while (st.hasMoreTokens()) + { + element = st.nextToken(); + while (st.hasMoreTokens() && element.equals(" ")) + { + element = st.nextToken(); + } + if (element.equals("]")) + tampon = tampon.trim() + "] "; + else if (element.equals("[")) + { + if (precedent.equals("[")) + tampon += "["; + else + tampon = tampon.trim() + " ["; + } + else + tampon += element + " "; + precedent = element; + } + return (tampon.trim()); + } + + private String extractList(StringTokenizer st) + { + int compteur = 1; + String crochet = "[ "; + String element = ""; + while (st.hasMoreTokens()) + { + element = st.nextToken(); + if (element.equals("[")) + { + compteur++; + crochet += "[ "; + } + else if (!element.equals("]")) + crochet += element + " "; + else if (compteur != 1) + { + compteur--; + crochet += "] "; + } + else + { + crochet += element + " "; + break; + } + } + element = crochet; + compteur = 0; + return element.trim(); + } + + private int extractList(String st, int deb) + { + int compteur = 1; + char element; + boolean espace = true; + boolean crochet_ouvert = false; + boolean crochet_ferme = false; + for (int i = deb; i < st.length(); i++) + { + element = st.charAt(i); + if (element == '[') + { + if (espace) + crochet_ouvert = true; + espace = false; + crochet_ferme = false; + } + else if (element == ']') + { + if (espace) + crochet_ferme = true; + espace = false; + crochet_ouvert = false; + } + else if (element == ' ') + { + espace = true; + if (crochet_ouvert) + { + compteur++; + crochet_ouvert = false; + } + else if (crochet_ferme) + { + crochet_ferme = false; + if (compteur != 1) + compteur--; + else + { + compteur = i; + break; + } + } + } + } + return compteur; + } + + // returns how many elements contains the list "liste" + private int numberOfElements(String liste) + { // calcule le nombre + // d'éléments dans une + // liste + StringTokenizer st = new StringTokenizer(liste); + int i = 0; + String element = ""; + while (st.hasMoreTokens()) + { + element = st.nextToken(); + if (element.equals("[")) + element = extractList(st); + i++; + } + return i; + } + + // returns the item "i" from the list "liste" + private String item(String liste, int i) throws LogoError + { // retourne + // l'élément i d'une + // liste + StringTokenizer st = new StringTokenizer(liste); + String element = ""; + int j = 0; + while (st.hasMoreTokens()) + { + j++; + element = st.nextToken(); + if (element.equals("[")) + element = extractList(st); + if (j == i) + break; + } + if (j != i) + throw new LogoError(Logo.messages.getString("y_a_pas") + " " + i + " " + + Logo.messages.getString("element_dans_liste") + liste + "]"); + else if (i == 0 && j == 0) + throw new LogoError(Logo.messages.getString("liste_vide")); + try + { + Double.parseDouble(element); + return element; + } // Si c'est un nombre, on le renvoie. + catch (Exception e) + {} + if (element.startsWith("[")) + return element + " "; // C'est une liste, on la renvoie telle + // quelle. + if (element.equals("\\v")) + element = ""; + return "\"" + element; // C'est forcément un mot, on le renvoie. + } + + // Test if the name of the variable is valid + private void isVariableName(String st) throws LogoError + { + if (st.equals("")) + throw new LogoError(Logo.messages.getString("variable_vide")); + if (":+-*/() []=<>&|".indexOf(st) > -1) + throw new LogoError(st + " " + Logo.messages.getString("erreur_variable")); + + try + { + Double.parseDouble(st); + throw new LogoError(Logo.messages.getString("erreur_nom_nombre_variable")); + } + catch (NumberFormatException e) + { + + } + + } + + // primitve make + private void donne(Stack<String> param) throws LogoError + { + String mot = getWord(param.get(0)); + if (null == mot) + throw new LogoError(param.get(0) + " " + Logo.messages.getString("error.word")); + mot = mot.toLowerCase(); + isVariableName(mot); + if (Interprete.locale.containsKey(mot)) + { + Interprete.locale.put(mot, param.get(1)); + } + else + { + wp.getGlobals().define(mot, param.get(1)); // TODO changed + } + } + + private void delay() + { + if (WSManager.getUserConfig().getTurtleSpeed() != 0) + { + try + { + Thread.sleep((long) (WSManager.getUserConfig().getTurtleSpeed()*3)); + } + catch (InterruptedException e) + {} + } + } + + // How many characters in the word "mot" + private int getWordLength(String mot) + {// retourne le nombre de caractères + // d'un mot + int compteur = 0; + boolean backslash = false; + for (int i = 0; i < mot.length(); i++) + { + if (!backslash && mot.charAt(i) == '\\') + backslash = true; + else + { + backslash = false; + compteur++; + } + } + return compteur; + } + + // the character number "i" in the word "mot" + private String itemWord(int entier, String mot) throws LogoError + { + String reponse = ""; + int compteur = 1; + boolean backslash = false; + if (mot.equals("")) + throw new LogoError(Logo.messages.getString("mot_vide")); + for (int i = 0; i < mot.length(); i++) + { + char c = mot.charAt(i); + if (!backslash && c == '\\') + backslash = true; + else + { + if (compteur == entier) + { + if (backslash) + reponse = "\\" + Character.toString(c); + else + reponse = Character.toString(c); + break; + } + else + { + compteur++; + backslash = false; + } + } + } + return reponse; + } + + protected void setWorkspace(UserSpace workspace) + { + wp = workspace; + } + + private void ou(Stack<String> param) throws LogoError + { + int size = param.size(); + boolean result = false; + boolean b; + for (int i = 0; i < size; i++) + { + b = predicat(param.get(i).toString()); + result = result | b; + } + if (result) + Interprete.calcul.push(Logo.messages.getString("vrai")); + else + Interprete.calcul.push(Logo.messages.getString("faux")); + Interprete.operande = true; + } + + private void et(Stack<String> param) throws LogoError + { + int size = param.size(); + boolean result = true; + boolean b; + for (int i = 0; i < size; i++) + { + b = predicat(param.get(i).toString()); + result = result & b; + } + Interprete.operande = true; + if (result) + Interprete.calcul.push(Logo.messages.getString("vrai")); + else + Interprete.calcul.push(Logo.messages.getString("faux")); + + } + + /** + * This methods returns a list that contains all procedures name + * + * @return A list with all procedure names + */ + private StringBuffer getAllProcedures() + { + StringBuffer sb = new StringBuffer("[ "); + // TODO CHANGED + for (Procedure proc : wp.getExecutables()) + { + sb.append(proc.getName()); + sb.append(" "); + } + sb.append("] "); + return sb; + } + + /** + * This methods returns a list that contains all variables name + * + * @return A list with all variables names + */ + + private StringBuffer getAllVariables() + { + StringBuffer sb = new StringBuffer("[ "); + Iterator<String> it = Interprete.locale.keySet().iterator(); + while (it.hasNext()) + { + String name = it.next(); + sb.append(name); + sb.append(" "); + } + // TODO changed + for (String key : wp.getGlobals().getVariables()) + { + if (!Interprete.locale.containsKey(key)) + { + sb.append(key.toString()); + sb.append(" "); + } + } + sb.append("] "); + return sb; + } + + /** + * This methods returns a list that contains all Property Lists name + * + * @return A list with all Property Lists names + */ + private StringBuffer getAllpropertyLists() + { + StringBuffer sb = new StringBuffer("[ "); + // TODO changed + for (String propList : wp.getPropertyLists().getPropListKeys()) + { + sb.append(propList); + sb.append(" "); + } + sb.append("] "); + return sb; + } + + /** + * Delete The variable called "name" from the workspace if it exists + * + * @param name + * The variable name + */ + private void deleteVariable(String name) + { + if (!Interprete.locale.isEmpty()) + { + if (Interprete.locale.containsKey(name)) + { + Interprete.locale.remove(name); + } + } + else + { + wp.getGlobals().deleteVariable(name.toLowerCase()); + } + } + + /** + * Delete the procedure called "name" from the workspace + * + * @param name + * The procedure name + */ + private void deleteProcedure(String name) + { + wp.eraseProcedure(name.toLowerCase()); + } + + /** + * According to the type of the data, erase from workspace the resource + * called "name" + * + * @param name + * The name of the deleted resource, it couls be a list with all + * resource names + * @param type + * The type for the data, it could be "variable", "procedure" or + * "propertylist" + * @throws LogoError + */ + + private void erase(String name, String type) throws LogoError + { + Interprete.operande = false; + if (LaunchPrimitive.isList(name)) + { + name = getFinalList(name); + StringTokenizer st = new StringTokenizer(name); + while (st.hasMoreTokens()) + { + String item = st.nextToken(); + this.eraseItem(item, type); + } + } + else + { + name = getWord(name); + if (null != name) + { + this.eraseItem(name, type); + } + else + throw new LogoError(name + Logo.messages.getString("error.word")); + + } + } + + /** + * According to the type of the data, erase from workspace the resource + * called "name" + * + * @param name + * The name of the deleted resource + * @param type + * The type for the data, it could be "variable", "procedure" or + * "propertylist" + */ + private void eraseItem(String name, String type) + { + if (type.equals("procedure")) + { + this.deleteProcedure(name.toLowerCase()); + } + else if (type.equals("variable")) + { + this.deleteVariable(name.toLowerCase()); + } + else if (type.equals("propertylist")) + { + wp.getPropertyLists().removePropList(name.toLowerCase()); + } + + } +} diff --git a/logo/src/xlogo/kernel/LogoError.java b/logo/src/xlogo/kernel/LogoError.java new file mode 100644 index 0000000..175c243 --- /dev/null +++ b/logo/src/xlogo/kernel/LogoError.java @@ -0,0 +1,89 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were entirely written Marko Zivkovic + */ + +package xlogo.kernel; + +/** + * In XLogo, myException was used. My exception was something rather funny. + * Actually it was really bad. myException was an exception that handled itself, by showing an error dialog and by aborting execution of the running Logo program. + * Thus throwing a myException was more like a function call with the side effect, that execution jumped to some other place, wherever it was caught. + * Note that to handle itself, myException had to have a reference to {@link Application}. Thus every part of the interpreter that wanted to throw Logo errors + * had to have such a reference, even though Application was not used otherwise.<br> + * By moving the exception handling at the root, where interpretation is started, in {@link Affichage}, I managed to decouple several classes from Application. + * (Still, many of them are unnecessarily dependent on Application, but I cannot refactor everything in the given time) + * + * Also note how ugly myException was used before. LaunchPrimitive is the best example. The general pattern was this:<br> + * + * <pre> + * {@code + * case i: // i : the id of some Logo primitive + * try + * { + * ... + * if (the next token is not as expected) + * throw new myException(application, errorMessage); + * ... + * } + * catch (myException) + * {} + * break; // => end of execute() + * // Because myException "handled itself", Affichage and Interprete will not continue execution + * } + * </pre> + * + * Note that almost all of the more than 300 Logo primitives contained such a statement, sometimes even more. + * That is more than 5x300 = 1500 unnecessary lines of code (after my style guide) that make the reading very hard, + * and introducing multiple levels of blocks { } to the structure. + * + * I took myself the time to remove all these unnecessary try-catches, place only one in Affichage and thus make the core of the application much more readable. + * <p><p> + * Note that I cannot test all the 300 Logo procedures for every case, but since throwing a myException caused the Interpreter to stop anyway, + * Redirecting the exception up to the root in Affichage will not change the flow of correct Logo Execution. Only executions which caused an exception can maybe behave slightly different now. + * I will for sure test all the procedures that we use in our school projects and a few more. + * + * <p><p> + * Another interesting thing, or again, really bad. + * The {@code static boolean lance} in myException was initialized with false. There are several assignments that set, again, false to it, but there is no single set to true. + * In most cases where lance is tested in a if-statement, it is tested in conjunction with Application.error (which can be true or false) + * + * <p> And then, there was also the nested class Affiche, which was never used. + * + *@author Marko Zivkovic + */ +public class LogoError extends Exception +{ + private static final long serialVersionUID = 9184760816698357437L; + + public LogoError() + { + } + + public LogoError(String st) + { + super(st); + } +} diff --git a/logo/src/xlogo/kernel/LoopFillPolygon.java b/logo/src/xlogo/kernel/LoopFillPolygon.java new file mode 100644 index 0000000..f2cb0fd --- /dev/null +++ b/logo/src/xlogo/kernel/LoopFillPolygon.java @@ -0,0 +1,48 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +package xlogo.kernel; + +import java.math.BigDecimal; + +public class LoopFillPolygon extends LoopProperties { + /** + * The super constructor for Fill Polygon Loops + * @param instr The instruction to execute each loop + */ + + LoopFillPolygon(){ + super(BigDecimal.ONE,BigDecimal.ONE,BigDecimal.ONE,""); + } + protected boolean isForEver(){ + return false; + } + protected boolean isFillPolygon(){ + return true; + } +} diff --git a/logo/src/xlogo/kernel/LoopFor.java b/logo/src/xlogo/kernel/LoopFor.java new file mode 100644 index 0000000..473fd28 --- /dev/null +++ b/logo/src/xlogo/kernel/LoopFor.java @@ -0,0 +1,94 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +package xlogo.kernel; +import java.math.BigDecimal; + +public class LoopFor extends LoopProperties{ + /** + * This boolean indicates + * @uml.property name="conserver" + */ + protected boolean conserver=false; + + /** + * The variable name + * @uml.property name="var" + */ + + String var=""; + /** + * Constructor Loop: For + * @param counter The beginning integer + * @param end The end integer + * @param increment The increment between two values + * @param instr The instruction to execute between two values + * @param var The name of the variable + */ + + LoopFor(BigDecimal counter,BigDecimal end,BigDecimal increment,String instr,String var){ + super(counter,end,increment,instr); + this.var=var; + } + + protected boolean isFor(){ + return true; + } + protected boolean isForEver(){ + return false; + } + /** + * This method affects the variable counter the correct value + * @param first boolean that indicates if it is the first affectation + */ + protected void AffecteVar(boolean first){ + String element=String.valueOf(super.getCounter()); + if (element.endsWith(".0")) element=element.substring(0,element.length()-2) ; + if (element.startsWith(".")||element.equals("")) element="0"+element; + + if (Interprete.locale.containsKey(var)){ + if (first) conserver=true; + Interprete.locale.put(var, element); + } + else { + Interprete.locale.put(var,element); + } + } + /** + * This method deletes the variable var from the local stack variable + */ + void DeleteVar(){ + if (!conserver){ + if (Interprete.locale.containsKey(var)){ + Interprete.locale.remove(var); + } + + } + } + +} diff --git a/logo/src/xlogo/kernel/LoopForEach.java b/logo/src/xlogo/kernel/LoopForEach.java new file mode 100644 index 0000000..3d0c518 --- /dev/null +++ b/logo/src/xlogo/kernel/LoopForEach.java @@ -0,0 +1,69 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +package xlogo.kernel; + +import java.math.BigDecimal; +import java.util.Vector; + +public class LoopForEach extends LoopFor{ + private Vector<String> vec; + /** + * Constructor Loop: For + * @param counter The beginning integer + * @param end The end integer + * @param increment The increment between two values + * @param instr The instruction to execute between two values + * @param var The name of the variable + * @param vec The Vec with all value for the variable + */ + LoopForEach(BigDecimal counter,BigDecimal end,BigDecimal increment,String instr,String var,Vector<String> vec){ + super(counter,end,increment,instr,var); + this.vec=vec; + } + protected boolean isForEach(){ + return true; + } + protected boolean isForEver(){ + return false; + } + /** + * This method affects the variable counter the correct value + * @param first boolean that indicates if it is the first affectation + */ + protected void AffecteVar(boolean first){ + String element=vec.get(getCounter().intValue()); + if (Interprete.locale.containsKey(var)){ + if (first) conserver=true; + Interprete.locale.put(var, element); + } + else { + Interprete.locale.put(var,element); + } + } +} diff --git a/logo/src/xlogo/kernel/LoopProperties.java b/logo/src/xlogo/kernel/LoopProperties.java new file mode 100644 index 0000000..ac252e4 --- /dev/null +++ b/logo/src/xlogo/kernel/LoopProperties.java @@ -0,0 +1,149 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +/** + * Title : XLogo + * Description : XLogo is an interpreter for the Logo + * programming language + * @author Loïc Le Coq + */ +package xlogo.kernel; +import java.math.BigDecimal; +/** + * This class saves all Loop Properties (repeat, while, for) such as increment, end integer .... + * @author loic + */ +public class LoopProperties { + /** + * Counter: The counter value for the current loop End: The end value for the loop Increment the increment between two values + * @uml.property name="counter" + */ + private BigDecimal counter; + /** + * Counter: The counter value for the current loop End: The end value for the loop Increment the increment between two values + * @uml.property name="end" + */ + private BigDecimal end; + /** + * Counter: The counter value for the current loop End: The end value for the loop Increment the increment between two values + * @uml.property name="increment" + */ + private BigDecimal increment; + /** + * The Instruction to execute on each iteration + * @uml.property name="instr" + */ + String instr; +/** + * The super constructor for all loops + * @param counter The beginning integer + * @param fin The end integer + * @param increment The increment between two values + * @param instr The instruction to execute each loop + */ + LoopProperties(BigDecimal counter,BigDecimal end,BigDecimal increment,String instr){ + this.counter=counter; + this.end=end; + this.increment=increment; + this.instr=instr; + } + + + /** + * Adds the increment to the variable counter + */ + + protected void incremente(){ + counter=counter.add(increment); + counter=new BigDecimal(MyCalculator.eraseZero(counter)); + } + /** + * This method returns the Loop Id + * @return the Loop Id (TYPE_FOR, TYPE_WHILE...) + */ +/* protected int getId(){ + return id; + }*/ + /** + * This method returns the Loop Id + * @return the Loop Id (TYPE_FOR, TYPE_WHILE...) + * @uml.property name="counter" + */ + protected BigDecimal getCounter(){ + return counter; + } + /** + * This method returns the end Value + * @return the end value for the loop + * @uml.property name="end" + */ + protected BigDecimal getEnd(){ + return end; + } + /** + * this method returns the increment for the loop + * @return The variable increment + * @uml.property name="increment" + */ + protected BigDecimal getIncrement(){ + return increment; + } + /** + * This method returns the instructions to execute each loop + * @return the instruction block + * @uml.property name="instr" + */ + protected String getInstr(){ + return instr; + } + /** + * This method returns a loop description + */ + public String toString(){ + return(counter+" "+end+" "+increment+"\n"+instr+"\n"); + } + + protected boolean isFor(){ + return false; + } + protected boolean isWhile(){ + return false; + } + protected boolean isForEach(){ + return false; + } + protected boolean isRepeat(){ + return false; + } + protected boolean isForEver(){ + return true; + } + protected boolean isFillPolygon(){ + return false; + } +} diff --git a/logo/src/xlogo/kernel/LoopRepeat.java b/logo/src/xlogo/kernel/LoopRepeat.java new file mode 100644 index 0000000..5362d46 --- /dev/null +++ b/logo/src/xlogo/kernel/LoopRepeat.java @@ -0,0 +1,43 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +package xlogo.kernel; + +import java.math.BigDecimal; + +public class LoopRepeat extends LoopProperties{ + LoopRepeat(BigDecimal counter,BigDecimal end,BigDecimal increment,String instr){ + super(counter,end,increment,instr); + } + protected boolean isRepeat(){ + return true; + } + protected boolean isForEver(){ + return false; + } +} diff --git a/logo/src/xlogo/kernel/LoopWhile.java b/logo/src/xlogo/kernel/LoopWhile.java new file mode 100644 index 0000000..c4451c8 --- /dev/null +++ b/logo/src/xlogo/kernel/LoopWhile.java @@ -0,0 +1,44 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +package xlogo.kernel; + +import java.math.BigDecimal; + +public class LoopWhile extends LoopProperties{ + + LoopWhile(BigDecimal counter,BigDecimal end,BigDecimal increment,String instr){ + super(counter,end,increment,instr); + } + protected boolean isForEver(){ + return false; + } + protected boolean isWhile(){ + return true; + } +} diff --git a/logo/src/xlogo/kernel/MP3Player.java b/logo/src/xlogo/kernel/MP3Player.java new file mode 100644 index 0000000..e996bd2 --- /dev/null +++ b/logo/src/xlogo/kernel/MP3Player.java @@ -0,0 +1,86 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +package xlogo.kernel; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.URL; + +import xlogo.Application; +import xlogo.Logo; +import xlogo.storage.WSManager; +import xlogo.utils.Utils; +import javazoom.jl.decoder.JavaLayerException; +import javazoom.jl.player.advanced.AdvancedPlayer; +public class MP3Player extends Thread{ + + private AdvancedPlayer player; + + MP3Player(Application app,String path) throws LogoError{ + try { + // Build absolutePath + String absolutePath= Utils.SortieTexte(WSManager.getUserConfig().getDefaultFolder()) + + File.separator + Utils.SortieTexte(path); + player=new AdvancedPlayer(new FileInputStream(absolutePath)); + } + catch(FileNotFoundException e){ + // tentative fichier réseau + try{ + URL url =new java.net.URL(path); + java.io.InputStream fr = url.openStream(); + player=new AdvancedPlayer(fr); + } + catch( java.net.MalformedURLException e1){ + + throw new LogoError(Logo.messages.getString("error.iolecture")); + + + } + catch(IOException e2){} + catch(JavaLayerException e3){} + } + catch(JavaLayerException e4){} + + } + public void run(){ + try{ + player.play(); + } + catch(JavaLayerException e){} + } + /** + * @return + * @uml.property name="player" + */ + protected AdvancedPlayer getPlayer(){ + return player; + } + +} diff --git a/logo/src/xlogo/kernel/MyCalculator.java b/logo/src/xlogo/kernel/MyCalculator.java new file mode 100644 index 0000000..1fde09c --- /dev/null +++ b/logo/src/xlogo/kernel/MyCalculator.java @@ -0,0 +1,1221 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +package xlogo.kernel; + +import java.math.MathContext; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.util.Stack; + +import xlogo.Logo; +import xlogo.utils.Utils; + +public class MyCalculator +{ + private final BigDecimal tenth = new BigDecimal(0.1); + + private MathContext mc = null; + + // If precision is lesser than 16 (operation for double) + /** + * @uml.property name="lowPrecision" + */ + private boolean lowPrecision = true; + + /** + * Indicates if the log table have been created + * + * @uml.property name="initLogTable" + */ + private boolean initLogTable; + /** + * This is a table containing all BigDecimal ln(1+10^ (-k) ), k in + * {0,1....,digits-1} This are constants for the Cordic method to calculate + * ln, exp + * + * @uml.property name="logTable" multiplicity="(0 -1)" dimension="1" + */ + private BigDecimal[] logTable; + /** + * Indicates if the trigonometric table have been created + * + * @uml.property name="initCosTable" + */ + private boolean initCosTable; + /** + * This is a table containing all BigDecimal arctan 10^ (-k) , k in + * {0,1....,digits-1} This are constants for the Cordic method to calculate + * trigonometric functions + * + * @uml.property name="cosTable" multiplicity="(0 -1)" dimension="1" + */ + private BigDecimal[] cosTable; + + private static int digits; + + protected MyCalculator(int digits) + { + MyCalculator.digits = digits; + initLogTable = false; + initCosTable = false; + if (digits < 16) + { + mc = new MathContext(16); + lowPrecision = true; + logTable = new BigDecimal[16]; + cosTable = new BigDecimal[16]; + } + else + { + mc = new MathContext(digits); + lowPrecision = false; + logTable = new BigDecimal[digits]; + cosTable = new BigDecimal[digits]; + } + } + + /** + * Return The exponential of s according to matContext Precision + * + * @param s + * The number + * @return Exp(s) + * @throws LogoError + * if s isn't a number + */ + + protected String exp(String s) throws LogoError + { + if (lowPrecision) + { + double nombre = numberDouble(s); + return teste_fin_double(Math.exp(nombre)); + } + else + { + BigDecimal bd = numberDecimal(s); + return expBD(bd).toPlainString(); + } + } + + /** + * Return The logarithm of s according to matContext Precision + * + * @param s + * The number + * @return log(s) + * @throws LogoError + * if s isn't a number or negative + */ + + protected String log(String s) throws LogoError + { + if (lowPrecision) + { + double nombre = numberDouble(s); + if (nombre < 0 || nombre == 0) + { + String log = Utils.primitiveName("arithmetic.log"); + throw new LogoError(log + " " + Logo.messages.getString("attend_positif")); + } + return teste_fin_double(Math.log(nombre)); + } + else + { + BigDecimal bd = numberDecimal(s); + if (bd.signum() != 1) + { + String log = Utils.primitiveName("arithmetic.log"); + throw new LogoError(log + " " + Logo.messages.getString("attend_positif")); + } + return logBD(bd).toPlainString(); + } + } + + /** + * Return The square root of s according to matContext Precision + * + * @param s + * The number + * @return sqrt(s) + * @throws LogoError + * if s isn't a number or negative + */ + + protected String sqrt(String s) throws LogoError + { + if (lowPrecision) + { + double number = numberDouble(s); + if (number < 0) + { + String sqrt = Utils.primitiveName("arithmetic.racine"); + throw new LogoError(sqrt + " " + Logo.messages.getString("attend_positif")); + } + return teste_fin_double(Math.sqrt(number)); + } + else + { + BigDecimal bd = numberDecimal(s); + if (bd.signum() == -1) + { + String sqrt = Utils.primitiveName("arithmetic.racine"); + throw new LogoError(sqrt + " " + Logo.messages.getString("attend_positif")); + } + return sqrtBD(bd).toPlainString(); + } + } + + /** + * Return the product of all elements in stack param + * + * @param param + * The stack of operands + * @return The product + * @throws LogoError + */ + protected String multiply(Stack<String> param) throws LogoError + { + int size = param.size(); + BigDecimal product = BigDecimal.ONE; + BigDecimal a; + for (int i = 0; i < size; i++) + { + a = numberDecimal(param.get(i)); + product = product.multiply(a, mc); + } + return product.stripTrailingZeros().toPlainString(); + } + + protected String divide(Stack<String> param) throws LogoError + { + if (lowPrecision) + { + double a = numberDouble(param.get(0)); + double b = numberDouble(param.get(1)); + if (b == 0) + throw new LogoError(Logo.messages.getString("division_par_zero")); + return (teste_fin_double(a / b)); + } + else + { + BigDecimal a = new BigDecimal(param.get(0), mc); + BigDecimal b = new BigDecimal(param.get(1), mc); + if (b.signum() == 0) + throw new LogoError(Logo.messages.getString("division_par_zero")); + return (a.divide(b, mc).stripTrailingZeros().toPlainString()); + } + } + + /** + * Return the sum of all elements in stack param + * + * @param param + * The stack of operands + * @return The sum + * @throws LogoError + */ + protected String add(Stack<String> param) throws LogoError + { + int size = param.size(); + BigDecimal sum = BigDecimal.ZERO; + BigDecimal a; + for (int i = 0; i < size; i++) + { + a = numberDecimal(param.get(i)); + sum = sum.add(a, mc); + } + + return sum.stripTrailingZeros().toPlainString(); + } + + protected String inf(Stack<String> param) throws LogoError + { + BigDecimal a = numberDecimal(param.get(0)); + BigDecimal b = numberDecimal(param.get(1)); + if (a.compareTo(b) < 0) + return Logo.messages.getString("vrai"); + return Logo.messages.getString("faux"); + } + + protected String sup(Stack<String> param) throws LogoError + { + BigDecimal a = numberDecimal(param.get(0)); + BigDecimal b = numberDecimal(param.get(1)); + if (a.compareTo(b) > 0) + return Logo.messages.getString("vrai"); + return Logo.messages.getString("faux"); + } + + protected String infequal(Stack<String> param) throws LogoError + { + BigDecimal a = numberDecimal(param.get(0)); + BigDecimal b = numberDecimal(param.get(1)); + if (a.compareTo(b) <= 0) + return Logo.messages.getString("vrai"); + return Logo.messages.getString("faux"); + } + + protected String supequal(Stack<String> param) throws LogoError + { + BigDecimal a = numberDecimal(param.get(0)); + BigDecimal b = numberDecimal(param.get(1)); + if (a.compareTo(b) >= 0) + return Logo.messages.getString("vrai"); + return Logo.messages.getString("faux"); + } + + protected String equal(Stack<String> param) throws LogoError + { + BigDecimal a = numberDecimal(param.get(0)); + BigDecimal b = numberDecimal(param.get(1)); + if (a.compareTo(b) == 0) + return Logo.messages.getString("vrai"); + return Logo.messages.getString("faux"); + } + + protected String substract(Stack<String> param) throws LogoError + { + BigDecimal a = numberDecimal(param.get(0)); + BigDecimal b = numberDecimal(param.get(1)); + return a.subtract(b, mc).stripTrailingZeros().toPlainString(); + } + + /** + * Returns the opposite of s + * + * @param s + * @return + */ + protected String minus(String s) throws LogoError + { + BigDecimal a = numberDecimal(s); + return a.negate(mc).stripTrailingZeros().toPlainString(); + } + + protected String remainder(String a, String b) throws LogoError + { + if (lowPrecision) + { + int aa = getInteger(a); + int bb = getInteger(b); + if (bb == 0) + throw new LogoError(Logo.messages.getString("division_par_zero")); + return teste_fin_double(aa % bb); + } + else + { + BigDecimal aa = getBigInteger(a); + BigDecimal bb = getBigInteger(b); + if (bb.signum() == 0) + throw new LogoError(Logo.messages.getString("division_par_zero")); + return aa.remainder(bb, mc).stripTrailingZeros().toPlainString(); + + } + } + + protected String modulo(String a, String b) throws LogoError + { + if (lowPrecision) + { + int aa = getInteger(a); + int bb = getInteger(b); + if (bb == 0) + throw new LogoError(Logo.messages.getString("division_par_zero")); + double rem = aa % bb; + if (aa * bb < 0 && rem != 0) + rem = rem + bb; + return teste_fin_double(rem); + } + else + { + BigDecimal aa = getBigInteger(a); + BigDecimal bb = getBigInteger(b); + if (bb.signum() == 0) + throw new LogoError(Logo.messages.getString("division_par_zero")); + BigDecimal rem = aa.remainder(bb, mc); + if (aa.multiply(bb).compareTo(BigDecimal.ZERO) == -1 && (!rem.equals(BigDecimal.ZERO))) + rem = rem.add(bb); + return rem.stripTrailingZeros().toPlainString(); + + } + } + + protected String quotient(String a, String b) throws LogoError + { + if (lowPrecision) + { + double aa = numberDouble(a); + double bb = numberDouble(b); + if (bb == 0) + throw new LogoError(Logo.messages.getString("division_par_zero")); + return String.valueOf((int) (aa / bb)); + } + else + { + BigDecimal aa = numberDecimal(a); + BigDecimal bb = numberDecimal(b); + if (bb.signum() == 0) + throw new LogoError(Logo.messages.getString("division_par_zero")); + return aa.divideToIntegralValue(bb, mc).stripTrailingZeros().toPlainString(); + + } + } + + protected String truncate(String a) throws LogoError + { + BigDecimal ent = numberDecimal(a); + return ent.toBigInteger().toString(); + } + + protected String abs(String a) throws LogoError + { + BigDecimal e = numberDecimal(a); + return e.abs().stripTrailingZeros().toPlainString(); + } + + protected String power(String a, String b) throws LogoError + { + if (lowPrecision) + { + double p = Math.pow(numberDouble(a), numberDouble(b)); + // Bug pr power -1 0.5 + Double p1 = new Double(p); + if (p1.equals(Double.NaN)) + throw new LogoError(Utils.primitiveName("arithmetic.puissance") + " " + + Logo.messages.getString("attend_positif")); + // End Bug + return teste_fin_double(p); + } + else + { + // if the exposant is an integer + try + { + int n = Integer.parseInt(b); + BigDecimal aa = numberDecimal(a); + return aa.pow(n, mc).toPlainString(); + } + catch (NumberFormatException e) + { + BigDecimal aa = numberDecimal(a); + BigDecimal bb = numberDecimal(b); + if (aa.signum() == 1) + { + return expBD(bb.multiply(logBD(aa), mc)).toPlainString(); + } + else if (aa.signum() == 0) + return "0"; + else + return String.valueOf(getInteger(b)); + } + } + } + + protected String log10(String s) throws LogoError + { + Stack<String> tmp = new Stack<String>(); + tmp.push(log(s)); + tmp.push(log("10")); + return divide(tmp); + } + + protected String pi() + { + if (lowPrecision) + { + return String.valueOf(Math.PI); + } + else + { + return piBD().toPlainString(); + } + } + + protected String sin(String s) throws LogoError + { + if (lowPrecision) + { + return teste_fin_double(Math.sin(Math.toRadians(numberDouble(s)))); + } + else + { + BigDecimal bd = numberDecimal(s); + return sinBD(bd).toPlainString(); + + } + } + + protected String cos(String s) throws LogoError + { + if (lowPrecision) + { + return teste_fin_double(Math.cos(Math.toRadians(numberDouble(s)))); + } + else + { + BigDecimal bd = numberDecimal(s); + return cosBD(bd).toPlainString(); + + } + } + + protected String tan(String s) throws LogoError + { + if (lowPrecision) + { + return teste_fin_double(Math.tan(Math.toRadians(numberDouble(s)))); + } + else + { + BigDecimal bd = numberDecimal(s); + return tanBD(bd).toPlainString(); + } + } + + protected String atan(String s) throws LogoError + { + if (lowPrecision) + { + return teste_fin_double(Math.toDegrees(Math.atan(numberDouble(s)))); + } + else + { + BigDecimal bd = numberDecimal(s); + return toDegree(atanBD(bd)).toPlainString(); + } + } + + protected String acos(String s) throws LogoError + { + if (lowPrecision) + { + return teste_fin_double(Math.toDegrees(Math.acos(numberDouble(s)))); + } + else + { + BigDecimal bd = numberDecimal(s); + return toDegree(acosBD(bd)).toPlainString(); + } + } + + protected String asin(String s) throws LogoError + { + if (lowPrecision) + { + return teste_fin_double(Math.toDegrees(Math.asin(numberDouble(s)))); + } + else + { + BigDecimal bd = numberDecimal(s); + return toDegree(asinBD(bd)).toPlainString(); + } + } + + /** + * This method returns the exp of bd + * based on the Cordic algorithm + * + * @param bd + * The first BigDecimal + * @return The result + */ + private BigDecimal expBD(BigDecimal bd) + { + if (!initLogTable) + { + initLogTable(); + } + int signum = bd.signum(); + if (signum == -1) + { + BigDecimal exp = expCordic(bd.negate(mc)); + exp = BigDecimal.ONE.divide(exp, mc); + return exp; + } + else if (signum == 0) + return BigDecimal.ONE; + else + { + return expCordic(bd); + } + } + + private BigDecimal expCordic(BigDecimal bd) + { + int i = 0; + BigDecimal y = BigDecimal.ONE; + while (i < mc.getPrecision()) + { + while (logTable[i].subtract(bd).signum() == -1) + { + bd = bd.subtract(logTable[i], mc); + y = y.add(y.multiply(tenth.pow(i, mc), mc), mc); + } + i++; + } + y = y.multiply(bd.add(BigDecimal.ONE, mc), mc); + return y; + + } + + /** + * This method returns the log of bd + * based on the Cordic algorithm + * + * @param bd + * The first BigDecimal + * @return The result + */ + private BigDecimal logBD(BigDecimal bd) + { + if (!initLogTable) + { + initLogTable(); + } + // If bd > 1 + int signum = bd.subtract(BigDecimal.ONE, mc).signum(); + if (signum == 1) + { + bd = bd.subtract(BigDecimal.ONE, mc); + return logCordic(bd); + } + else if (signum == 0) + return BigDecimal.ZERO; + else + { + bd = BigDecimal.ONE.divide(bd, mc).subtract(BigDecimal.ONE, mc); + return logCordic(bd).negate(mc); + } + } + + private BigDecimal logCordic(BigDecimal bd) + { + int i = 0; + BigDecimal y = BigDecimal.ZERO; + while (i < mc.getPrecision()) + { + BigDecimal tenthi = tenth.pow(i, mc); + while (bd.subtract(tenthi, mc).signum() > 0) + { + bd = bd.subtract(tenthi, mc).divide(BigDecimal.ONE.add(tenthi, mc), mc); + y = y.add(logTable[i], mc); + } + i++; + } + y = y.add(bd, mc).subtract(bd.pow(2, mc).multiply(new BigDecimal(0.5), mc), mc); + return y; + + } + + /** + * This method returns the sqrt of bd + * based on the Cordic algorithm + * + * @param bd + * The first BigDecimal + * @return The result + */ + private BigDecimal sqrtBD(BigDecimal bd) + { + if (bd.signum() == 0) + return BigDecimal.ZERO; + BigDecimal three = new BigDecimal(3); + BigDecimal half = new BigDecimal(0.5); + BigDecimal x = BigDecimal.ZERO; + BigDecimal y = BigDecimal.ONE.min(BigDecimal.ONE.divide(bd, mc)); + while (x.compareTo(y) == -1) + { + x = y; + // y=(3x-bd*x^3)/2 + y = x.multiply(three, mc).subtract(bd.multiply(x.pow(3, mc), mc), mc).multiply(half, mc); + } + return BigDecimal.ONE.divide(y, mc); + } + + /** + * This method returns the cos of bd + * based on the Cordic algorithm + * + * @param bd + * The first BigDecimal + * @return The result + */ + private BigDecimal cosBD(BigDecimal bd) + { + // bd is in degree + BigDecimal period = new BigDecimal(360); + BigDecimal a90 = new BigDecimal(90); + BigDecimal a135 = new BigDecimal(135); + BigDecimal a180 = new BigDecimal(180); + BigDecimal a225 = new BigDecimal(225); + BigDecimal a270 = new BigDecimal(270); + BigDecimal a315 = new BigDecimal(315); + + bd = bd.remainder(period, mc); + if (bd.signum() == -1) + bd = bd.add(period, mc); + BigDecimal quarterpi = new BigDecimal(45); + // Now bd between 0 and 360. + if (bd.compareTo(quarterpi) == -1) + { + // Between 0 and 45 + return cosCordic(toRadian(bd)); + } + else if (bd.compareTo(a90) == -1) + { + // Between 45 and 90 + return sinCordic(toRadian(a90.subtract(bd, mc))); + } + else if (bd.compareTo(a135) == -1) + { + // Between 90 and 135 + return sinCordic(toRadian(bd.subtract(a90, mc))).negate(mc); + } + else if (bd.compareTo(a180) == -1) + { + // Between 135 and 180 + return cosCordic(toRadian(a180.subtract(bd, mc))).negate(mc); + } + else if (bd.compareTo(a225) == -1) + { + // Between 180 and 225 + return cosCordic(toRadian(bd.subtract(a180, mc))).negate(mc); + } + else if (bd.compareTo(a270) == -1) + { + // Between 225 and 270 + return sinCordic(toRadian(a270.subtract(bd, mc))).negate(mc); + } + else if (bd.compareTo(a315) == -1) + { + // Between 270 and 315 + return sinCordic(toRadian(bd.subtract(a270, mc))); + } + else + { + return cosCordic(toRadian(new BigDecimal(360).subtract(bd, mc))); + } + } + + /** + * This method returns the cos of bd with 0<bd<pi/4 + * based on the Cordic algorithm + * + * @param bd + * The first BigDecimal + * @return The result + */ + private BigDecimal cosCordic(BigDecimal bd) + { + return BigDecimal.ONE.divide(sqrtBD(tanCordic(bd).pow(2, mc).add(BigDecimal.ONE, mc)), mc); + } + + /** + * This method returns the cos of bd + * based on the Cordic algorithm + * + * @param bd + * The first BigDecimal + * @return The result + */ + private BigDecimal sinBD(BigDecimal bd) + { + BigDecimal a90 = new BigDecimal(90); + return cosBD(a90.subtract(bd, mc)); + + } + + /** + * This method returns the sin of bd with 0<bd<pi/4 + * based on the Cordic algorithm + * + * @param bd + * The first BigDecimal + * @return The result + */ + private BigDecimal sinCordic(BigDecimal bd) + { + BigDecimal tan = tanCordic(bd); + return tan.divide(sqrtBD(tan.pow(2, mc).add(BigDecimal.ONE, mc)), mc); + } + + /** + * This method returns the tan of bd (in degree) + * based on the Cordic algorithm + * + * @param bd + * The first BigDecimal + * @return The result + */ + private BigDecimal tanBD(BigDecimal bd) + { + // bd is in degree + BigDecimal pi = new BigDecimal(180); + BigDecimal halfpi = new BigDecimal(90); + BigDecimal quarterpi = new BigDecimal(45); + bd = bd.remainder(pi, mc); + if (bd.compareTo(halfpi.negate(mc)) == -1) + bd = bd.add(pi, mc); + if (bd.compareTo(halfpi) == 1) + bd = bd.subtract(pi, mc); + // Now bd is in -90;+90 degrees + + if (bd.compareTo(quarterpi) == 1) + { + BigDecimal x = toRadian(new BigDecimal(0.5).multiply(bd, mc)); + return new BigDecimal(2).multiply(tanCordic(x), mc).divide( + BigDecimal.ONE.subtract(tanCordic(x).pow(2, mc), mc), mc); + } + else if (bd.signum() == 1) + { + return tanCordic(toRadian(bd)); + } + else if (bd.compareTo(quarterpi.negate(mc)) == 1) + { + return tanCordic(toRadian(bd.negate(mc))).negate(mc); + } + else + { + BigDecimal x = toRadian(new BigDecimal(0.5).multiply(bd, mc)).negate(mc); + return new BigDecimal(2).multiply(tanCordic(x), mc) + .divide(BigDecimal.ONE.subtract(tanCordic(x).pow(2, mc), mc), mc).negate(mc); + } + } + + /** + * This method returns the tan of bd with 0<bd<pi/4 + * based on the Cordic algorithm + * + * @param bd + * The first BigDecimal + * @return The result + */ + private BigDecimal tanCordic(BigDecimal bd) + { + if (!initCosTable) + initCosTable(); + BigDecimal three = new BigDecimal(3); + int k = 1; + BigDecimal x = BigDecimal.ONE; + BigDecimal y = BigDecimal.ZERO; + BigDecimal tenthk = tenth; + while (k < mc.getPrecision()) + { + while (cosTable[k].compareTo(bd) == -1) + { + bd = bd.subtract(cosTable[k], mc); + BigDecimal tmp = x; + x = x.subtract(tenthk.multiply(y, mc), mc); + y = y.add(tenthk.multiply(tmp, mc), mc); + } + tenthk = tenthk.multiply(tenth, mc); + k++; + } + BigDecimal tmp = bd.pow(3, mc).add(three.multiply(bd, mc), mc); + // return (3*y+(3t+t^3)*x)/(3x-(3t+t^3)*y + return three.multiply(y, mc).add(x.multiply(tmp, mc), mc) + .divide(three.multiply(x, mc).subtract(y.multiply(tmp, mc), mc), mc); + } + + private BigDecimal piBD() + { + if (!initCosTable) + { + initCosTable(); + } + return cosTable[0].multiply(new BigDecimal(4), mc); + } + + /** + * This method creates the log Table using + * log h=(h-1)-(h-1)^2/2+(h-1)^3/3-..... + * + * @param bd + * The first BigDecimal + * @return The result + */ + private void initLogTable() + { + initLogTable = true; + // calculate ln 2 + // Using ln 2=2*(x+x^3/3+x^5/5+....) with x=1/3 + + BigDecimal sum = BigDecimal.ZERO; + BigDecimal previous = BigDecimal.ONE; + BigDecimal i = BigDecimal.ONE; + BigDecimal nine = new BigDecimal(9); + BigDecimal two = new BigDecimal(2); + BigDecimal power = new BigDecimal(3); + while (sum.subtract(previous, mc).abs(mc).compareTo(BigDecimal.ZERO) != 0) + { + previous = sum; + sum = sum.add(BigDecimal.ONE.divide(i.multiply(power, mc), mc), mc); + i = i.add(two, mc); + power = power.multiply(nine, mc); + } + logTable[0] = sum.multiply(two, mc); + + // Calculate ln (1+10^-j) j in 1 ... digits-1 + + for (int j = 1; j < mc.getPrecision(); j++) + { + // count=0; + sum = BigDecimal.ZERO; + previous = BigDecimal.ONE; + i = BigDecimal.ONE; + // 10^(-j) + BigDecimal bd = tenth.pow(j, mc); + power = bd; + while (sum.subtract(previous, mc).abs(mc).compareTo(BigDecimal.ZERO) != 0) + { + previous = sum; + sum = sum.add(power.divide(i, mc), mc); + if (i.signum() == 1) + i = i.add(BigDecimal.ONE, mc).negate(mc); + else + i = i.subtract(BigDecimal.ONE, mc).negate(mc); + power = power.multiply(bd, mc); + // count++; + } + logTable[j] = sum; + } + } + + /** + * This method creates the cos Table using + * arctan h=x-x^3/3+x^5/5-x^7/7... + * + * @param bd + * The first BigDecimal + * @return The result + */ + private void initCosTable() + { + initCosTable = true; + // calculate pi/4 + + cosTable[0] = calcPI().multiply(new BigDecimal(0.25), mc); + + // Calculate arctan (10^-j) j in 1 ... digits-1 + + for (int j = 1; j < mc.getPrecision(); j++) + { + // 10^(-j) + BigDecimal bd = tenth.pow(j, mc); + cosTable[j] = arctanSE(bd); + // System.out.println(cosTable[j].toPlainString()); + } + } + + // Using PI = 16arctg(1/5) - 4arctg(1/239) + private BigDecimal calcPI() + { + return new BigDecimal(16).multiply(arctanSE(new BigDecimal("0.2")), mc).subtract( + new BigDecimal(4).multiply(arctanSE(BigDecimal.ONE.divide(new BigDecimal(239), mc)), mc), mc); + } + + private BigDecimal arctanSE2(BigDecimal bd) + { + BigDecimal i = BigDecimal.ONE; + // BigDecimal j=new BigDecimal(3); + BigDecimal two = new BigDecimal(2); + BigDecimal square = bd.multiply(bd, mc); + BigDecimal power = bd.divide(square.add(BigDecimal.ONE, mc), mc); + BigDecimal cst = new BigDecimal(4).multiply(square, mc).divide(square.add(BigDecimal.ONE, mc), mc); + BigDecimal previous = BigDecimal.ZERO; + BigDecimal sum = power; + int count = 0; + while (sum.subtract(previous, mc).abs(mc).compareTo(BigDecimal.ZERO) != 0) + { + previous = sum; + power = power.multiply(cst, mc); + power = power.multiply(i.pow(2, mc), mc); + BigDecimal doublei = two.multiply(i, mc); + doublei = doublei.multiply(doublei.add(BigDecimal.ONE, mc), mc); + power = power.divide(doublei, mc); + sum = sum.add(power, mc); + i = i.add(BigDecimal.ONE); + count++; + } + System.out.println("Itérations " + count); + return sum; + + } + + private BigDecimal atanBD(BigDecimal bd) + { + if (bd.signum() == -1) + return atanBD(bd.negate(mc)).negate(mc); + if (bd.compareTo(BigDecimal.ONE) == 1) + // pi/2 -arctan (1/x) + return piBD().multiply(new BigDecimal(0.5), mc).subtract(arctanSE(BigDecimal.ONE.divide(bd, mc)), mc); + else if (bd.compareTo(BigDecimal.ONE) == 0) + return piBD().multiply(new BigDecimal("0.25"), mc); + else + return arctanSE(bd); + } + + private BigDecimal acosBD(BigDecimal bd) + { + if (bd.compareTo(new BigDecimal("-1")) == 0) + { + return piBD(); + } + // acos x= 2 atan (sqrt(1-x^2)/1+x + else + { + return new BigDecimal("2").multiply( + atanBD(sqrtBD(BigDecimal.ONE.subtract(bd.pow(2, mc), mc)).divide(BigDecimal.ONE.add(bd, mc), mc)), + mc); + + } + } + + private BigDecimal asinBD(BigDecimal bd) + { + // acos x= 2 atan (x/(1+sqrt(1-x^2)) + return new BigDecimal("2").multiply( + atanBD(bd.divide(BigDecimal.ONE.add(sqrtBD(BigDecimal.ONE.subtract(bd.pow(2, mc), mc)), mc), mc)), mc); + } + + // arctan h=x-x^3/3+x^5/5-x^7/7... + private BigDecimal arctanSE(BigDecimal bd) + { + BigDecimal i = BigDecimal.ONE; + BigDecimal two = new BigDecimal(2, mc); + BigDecimal square = bd.multiply(bd, mc); + BigDecimal sum = BigDecimal.ZERO; + BigDecimal previous = BigDecimal.ONE; + while (sum.subtract(previous, mc).abs(mc).compareTo(BigDecimal.ZERO) != 0) + { + previous = sum; + sum = sum.add(bd.divide(i, mc), mc); + if (i.signum() == 1) + i = i.add(two, mc).negate(mc); + else + i = i.subtract(two, mc).negate(mc); + bd = bd.multiply(square, mc); + } + return sum; + } + + private BigDecimal toRadian(BigDecimal n) + { + return n.multiply(piBD(), mc).divide(new BigDecimal(180), mc); + } + + private BigDecimal toDegree(BigDecimal n) + { + return n.multiply(new BigDecimal(180), mc).divide(piBD(), mc); + } + + /** + * This method converts st to double + * + * @param st + * The String + * @return The double corresponding to st + * @throws LogoError + * If st can't be convert + */ + + protected double numberDouble(String st) throws LogoError + { // Si un nombre est + // un double + try + { + return (Double.parseDouble(st)); + } + catch (NumberFormatException e) + { + throw new LogoError(st + " " + Logo.messages.getString("pas_nombre")); + } + } + + /** + * If a double ends with the suffix ".0", remove it + * + * @param d + * @return + */ + + static protected String teste_fin_double(double d) + { + String st = String.valueOf(d); + if (st.endsWith(".0")) + st = st.substring(0, st.length() - 2); + return st; + } + + /** + * Converts st to BigDecimal number + * + * @param st + * The String to convert + * @return The BigDecimal Number + * @throws LogoError + * if st isn't a number + */ + + protected BigDecimal numberDecimal(String st) throws LogoError + { + /* + * if (null==mc){ + * try { + * BigDecimal bd = new BigDecimal(st).setScale(16, + * BigDecimal.ROUND_HALF_EVEN); + * return (new BigDecimal(eraseZero(bd))); + * } catch (NumberFormatException e) { + * throw new myException( st + " " + * + Logo.messages.getString("pas_nombre")); + * } + * } + * else { + */ + try + { + return new BigDecimal(st, mc); + } + catch (NumberFormatException e) + { + throw new LogoError(st + " " + Logo.messages.getString("pas_nombre")); + } + // } + + } + + /** + * Erase unused Zeros in decimal Format + * + * @param bd + * The decimal number + * @return The formatted number + */ + static protected String eraseZero(BigDecimal bd) + { + DecimalFormatSymbols dfs = new DecimalFormatSymbols(); + dfs.setDecimalSeparator('.'); + DecimalFormat df = new DecimalFormat("#####.################", dfs); + String st = df.format(bd); + return st; + + } + + /** + * Test if the number contained in st is an integer + * + * @param st + * The Object to convert + * @return The integer corresponding to st + * @throws LogoError + * If it isn't an integer + */ + + protected int getInteger(String st) throws LogoError + { // Si c'est un + // entier + try + { + return Integer.parseInt(st); + } + catch (NumberFormatException e) + { + throw new LogoError(st + " " + Logo.messages.getString("pas_entier")); + } + } + + /** + * Test if the number contained in st is an integer + * + * @param st + * The Object to convert + * @return The integer corresponding to st + * @throws LogoError + * If it isn't an integer + */ + + protected BigDecimal getBigInteger(String st) throws LogoError + { // Si c'est un + // entier + try + { + return new BigDecimal(new BigInteger(st)); + } + catch (NumberFormatException e) + { + throw new LogoError(st + " " + Logo.messages.getString("pas_entier")); + } + } + + protected int getDigits() + { + if (digits < 0) + return -1; + else + return mc.getPrecision(); + } + + public static String getOutputNumber(String s) + { + try + { + if (digits >= 0 && digits < 16) + { + BigDecimal bd = new BigDecimal(s); + s = bd.toPlainString(); + // is it a decimal number? + int index = s.indexOf("."); + if (index != -1) + { + if (digits == 0) + return s.substring(0, index); + else if (s.length() > index + digits) + { + s = s.substring(0, index + digits + 1); + int a = Integer.parseInt(String.valueOf(s.charAt(s.length() - 1))); + if (a > 4) + { + a++; + s = s.substring(0, s.length() - 1) + a; + } + return s; + } + } + else + return s; + } + } + catch (NumberFormatException e) + {} + return s; + } +} diff --git a/logo/src/xlogo/kernel/MyFlow.java b/logo/src/xlogo/kernel/MyFlow.java new file mode 100644 index 0000000..2c7b5e3 --- /dev/null +++ b/logo/src/xlogo/kernel/MyFlow.java @@ -0,0 +1,110 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +package xlogo.kernel; + +public class MyFlow +{ + + private String path; + private int id; + private boolean finished; + + MyFlow(int id, String path, boolean finished) + { + this.id = id; + this.path = path; + this.finished = finished; + } + + MyFlow(MyFlow flow) + { + this.id = flow.getId(); + this.path = flow.getPath(); + this.finished = flow.isFinished(); + } + + /** + * @return + */ + String getPath() + { + return path; + } + + /** + * @param p + */ + void setPath(String p) + { + path = p; + } + + /** + * @return + */ + int getId() + { + return id; + } + + /** + * @param i + */ + void setId(int i) + { + id = i; + } + + /** + * @return + */ + boolean isFinished() + { + return finished; + } + + /** + * @param b + */ + void setFinished(boolean b) + { + finished = b; + } + + boolean isReader() + { + return false; + } + + boolean isWriter() + { + return false; + } + +} diff --git a/logo/src/xlogo/kernel/MyFlowReader.java b/logo/src/xlogo/kernel/MyFlowReader.java new file mode 100644 index 0000000..2b3409b --- /dev/null +++ b/logo/src/xlogo/kernel/MyFlowReader.java @@ -0,0 +1,81 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +package xlogo.kernel; + +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; + +public class MyFlowReader extends MyFlow +{ + BufferedReader bfr; + + boolean isReader() + { + return true; + } + + MyFlowReader(MyFlow flow) + { + super(flow); + } + + String readLine() throws FileNotFoundException, IOException + { + if (null == bfr) + bfr = new BufferedReader(new FileReader(getPath())); + String line = bfr.readLine(); + return line; + } + + int readChar() throws FileNotFoundException, IOException + { + if (null == bfr) + bfr = new BufferedReader(new FileReader(getPath())); + int character = bfr.read(); + return character; + } + + int isReadable() throws FileNotFoundException, IOException + { + if (null == bfr) + bfr = new BufferedReader(new FileReader(getPath())); + bfr.mark(2); + int id = bfr.read(); + bfr.reset(); + return id; + } + + void close() throws IOException + { + if (null != bfr) + bfr.close(); + } +} diff --git a/logo/src/xlogo/kernel/MyFlowWriter.java b/logo/src/xlogo/kernel/MyFlowWriter.java new file mode 100644 index 0000000..7651aee --- /dev/null +++ b/logo/src/xlogo/kernel/MyFlowWriter.java @@ -0,0 +1,74 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +package xlogo.kernel; + +import java.io.BufferedWriter; +import java.io.FileNotFoundException; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; + +import xlogo.utils.Utils; + +public class MyFlowWriter extends MyFlow +{ + BufferedWriter bfw; + + MyFlowWriter(MyFlow flow) + { + super(flow); + } + + void append(String line) throws FileNotFoundException, IOException + { + if (null == bfw) + bfw = new BufferedWriter(new FileWriter(getPath(), true)); + PrintWriter pw = new PrintWriter(bfw); + pw.println(Utils.SortieTexte(line)); + } + + void write(String line) throws FileNotFoundException, IOException + { + if (null == bfw) + bfw = new BufferedWriter(new FileWriter(getPath())); + PrintWriter pw = new PrintWriter(bfw); + pw.println(Utils.SortieTexte(line)); + } + + boolean isWriter() + { + return true; + } + + void close() throws IOException + { + if (null != bfw) + bfw.close(); + } +} diff --git a/logo/src/xlogo/kernel/Primitive.java b/logo/src/xlogo/kernel/Primitive.java new file mode 100644 index 0000000..0d0d8d1 --- /dev/null +++ b/logo/src/xlogo/kernel/Primitive.java @@ -0,0 +1,573 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +/** + * Title : XLogo Description : XLogo is an interpreter for the Logo programming + * language + * + * @author Loïc Le Coq + */ +package xlogo.kernel; + +import java.util.Vector; +import java.util.Collections; +import java.util.Locale; +import java.util.ResourceBundle; +import java.util.Stack; +import java.util.StringTokenizer; +import java.util.TreeMap; +import java.math.BigDecimal; + +import xlogo.kernel.LoopProperties; +import xlogo.messages.async.history.HistoryMessenger; +import xlogo.storage.WSManager; +import xlogo.storage.workspace.Language; +import xlogo.utils.Utils; +import xlogo.Application; + +import java.io.*; + +import xlogo.Logo; + +import java.util.Enumeration; + +public class Primitive +{ + /** + * This character indicates the end of a procedure in instructionBuffer + */ + protected static final String END_PROCEDURE = "\n"; + /** + * This character indicates the end of a loop in instructionBuffer + */ + protected static final String END_LOOP = "\\"; + + // float taille_crayon=(float)0; + private Application app; + protected static final int PRIMITIVE_NUMBER = 310; + protected static int[] parametres = new int[PRIMITIVE_NUMBER]; + protected static boolean[] generalForm = new boolean[PRIMITIVE_NUMBER]; + + // Treemap for primitives (better efficiency in searching) + public static TreeMap<String, String> primitives = new TreeMap<String, String>(); + + public static Stack<LoopProperties> stackLoop = new Stack<LoopProperties>(); + + public Primitive() + { + } + + public Primitive(Application app) + { + this.app = app; + // build treemap for primitives + buildPrimitiveTreemap(WSManager.getInstance().getWorkspaceConfigInstance().getLanguage()); + } + + /** + * This methods returns a list which contains all the primitive for the + * current language + * + * @return The primitives list + */ + protected String getAllPrimitives() + { + Vector<String> list = new Vector<String>(); + Locale locale = WSManager.getInstance().getWorkspaceConfigInstance().getLanguage().getLocale(); + ResourceBundle prim = ResourceBundle.getBundle("primitives", locale); + try + { + BufferedReader bfr = new BufferedReader(new InputStreamReader( + Primitive.class.getResourceAsStream("genericPrimitive"))); + while (bfr.ready()) + { + String line = bfr.readLine(); + // read the generic keyword for the primitive + StringTokenizer cut = new StringTokenizer(line); + // read the standard number of arguments for the primitive + String cle = cut.nextToken(); + // Exclude internal primitive \n \x and siwhile + if (!cle.equals("\\n") && !cle.equals("\\siwhile") && !cle.equals("\\x")) + { + // Exclude all arithmetic symbols + - / * & | + if (cle.length() != 1) + { + if (!cle.equals(">=") && !cle.equals("<=")) + { + cle = prim.getString(cle).trim(); + list.add(cle); + } + } + } + } + } + catch (IOException e) + { + System.out.println("Impossible de lire le fichier d'initialisation des primitives"); + } + Collections.sort(list); + StringBuffer sb = new StringBuffer("[ "); + for (int i = 0; i < list.size(); i++) + { + sb.append("[ "); + sb.append(list.get(i)); + sb.append("] "); + } + sb.append("] "); + return (sb.toString()); + } + + // Exécution des primitives + public static void buildPrimitiveTreemap(Language lang) + { + // this.exportPrimCSV(); + primitives = new TreeMap<String, String>(); + Locale locale = lang.getLocale(); + ResourceBundle prim = ResourceBundle.getBundle("primitives", locale); + try + { + BufferedReader bfr = new BufferedReader(new InputStreamReader( + Primitive.class.getResourceAsStream("genericPrimitive"))); + int i = 0; + while (bfr.ready()) + { + String line = bfr.readLine(); + // read the generic keyword for the primitive + StringTokenizer cut = new StringTokenizer(line); + // read the standard number of arguments for the primitive + String cle = cut.nextToken(); + parametres[i] = Integer.parseInt(cut.nextToken()); + // Read if the primitive has a general form + // eg (sum 2 3 4 5) --> 14 + // eg (list 3 4 5) ---> [3 4 5] + if (cut.hasMoreTokens()) + { + generalForm[i] = true; + } + else + generalForm[i] = false; + + if (i == 39) + primitives.put("\n", "39"); + // Internal Primitive siwhile + else if (i == 95) + { + primitives.put("\\siwhile", "95"); + } + // Internal Primitive \x + else if (i == 212) + primitives.put("\\x", "212"); + else + { + if (cle.length() != 1) + { + if (!cle.equals(">=") && !cle.equals("<=")) + cle = prim.getString(cle); + } + StringTokenizer st = new StringTokenizer(cle.toLowerCase()); + int compteur = 0; + while (st.hasMoreTokens()) + { + primitives.put(st.nextToken(), String.valueOf(i + Primitive.PRIMITIVE_NUMBER * compteur)); + compteur++; + } + } + i++; + } + /* + * System.out.println(i+ " primitives"); + * java.util.Iterator it=primitives.keySet().iterator(); + * while(it.hasNext()){ + * String next=it.next().toString(); + * System.out.println(next+" "+primitives.get(next)); + * } + */ + } + catch (IOException e) + { + System.out.println("Impossible de lire le fichier d'initialisation des primitives"); + } + } + + /** + * This method creates the loop "repeat" + * + * @param i + * The number of iteration + * @param st + * The instruction to execute + * @throws LogoError + */ + protected void repete(int i, String st) throws LogoError + { + if (i > 0) + { + st = new String(Utils.decoupe(st)); + LoopProperties bp = new LoopRepeat(BigDecimal.ONE, new BigDecimal(i), BigDecimal.ONE, st); + stackLoop.push(bp); + app.getKernel().getInstructionBuffer().insert(st + "\\ "); + } + else if (i != 0) { throw new LogoError(Utils.primitiveName("controls.repete") + " " + + Logo.messages.getString("attend_positif")); } + } + + /** + * This method is an internal primitive for the primitive "while" + * + * @param b + * Do we still execute the loop body? + * @param li + * The loop body instructions + * @throws LogoError + */ + protected void whilesi(boolean b, String li) throws LogoError + { + if (b) + { + app.getKernel().getInstructionBuffer().insert(li + Primitive.stackLoop.peek().getInstr()); + } + else + { + eraseLevelStop(app); + } + } + + // primitive if + protected void si(boolean b, String li, String li2) + { + if (b) + { + app.getKernel().getInstructionBuffer().insert(li); + } + else if (null != li2) + { + app.getKernel().getInstructionBuffer().insert(li2); + } + } + + // primitive stop + protected void stop() throws LogoError + { + Interprete.operande = false; + String car = ""; + car = eraseLevelStop(app); + + // A procedure has been stopped + if (car.equals("\n")) + { + String en_cours = Interprete.en_cours.pop(); + Interprete.locale = Interprete.stockvariable.pop(); + // Example: to bug + // fd stop + // end + // -------- + // bug + // stop doesn't output to fd + if (!Interprete.nom.isEmpty() && !Interprete.nom.peek().equals("\n")) + { + // System.out.println(Interprete.nom); + throw new LogoError(Utils.primitiveName("controls.stop") + " " + + Logo.messages.getString("ne_renvoie_pas") + " " + Interprete.nom.peek()); + } + else if (!Interprete.nom.isEmpty()) + { + // Removing the character "\n" + Interprete.nom.pop(); + // Example: to bug | to bug2 + // fd bug2 | stop + // end | end + // ------------------------ + // bug + // bug2 doesn't output to fd + if (!Interprete.nom.isEmpty() && !Interprete.nom.peek().equals("\n")) + { + // System.out.println(Interprete.nom); + throw new LogoError(en_cours + " " + Logo.messages.getString("ne_renvoie_pas") + " " + + Interprete.nom.peek()); + } + } + } + } + + // primitive output + protected void retourne(String val) throws LogoError + { + Interprete.calcul.push(val); + Interprete.operande = true; + if (Kernel.mode_trace) + { + StringBuffer buffer = new StringBuffer(); + for (int i = 0; i < Interprete.en_cours.size() - 1; i++) + buffer.append(" "); + buffer.append(Interprete.en_cours.peek()); + buffer.append(" " + Utils.primitiveName("ret") + " " + val); + HistoryMessenger.getInstance().dispatchMessage(Utils.SortieTexte(buffer.toString()) + "\n"); + } + Interprete.en_cours.pop(); + Interprete.locale = Interprete.stockvariable.pop(); + if ((!Interprete.nom.isEmpty()) && Interprete.nom.peek().equals("\n")) + { + eraseLevelReturn(app); + Interprete.nom.pop(); + } + else if (!Interprete.nom.isEmpty()) + throw new LogoError(Utils.primitiveName("ret") + " " + Logo.messages.getString("ne_renvoie_pas") + " " + + Interprete.nom.peek()); + else + throw new LogoError(Logo.messages.getString("erreur_retourne")); + } + + /** + * This method deletes all instruction since it encounters the end of a loop + * or the end of a procedure + * + * @param app + * The runnning frame Application + * @return The specific character \n or \ if found + * @throws LogoError + */ + private String eraseLevelStop(Application app) throws LogoError + { + boolean error = true; + String caractere = ""; + int marqueur = 0; + InstructionBuffer instruction = app.getKernel().getInstructionBuffer(); + for (int i = 0; i < instruction.getLength(); i++) + { + caractere = String.valueOf(instruction.charAt(i)); + if (caractere.equals(Primitive.END_LOOP) | caractere.equals(Primitive.END_PROCEDURE)) + { + marqueur = i; + if (caractere.equals(Primitive.END_LOOP) && i != instruction.getLength() - 1) + { + /* + * On test si le caractère "\" est bien un caractère de + * fin + * de boucle et non du style "\e" ou "\#" + */ + if (instruction.charAt(i + 1) == ' ') + { + error = false; + break; + } + } + else + { + error = false; + break; + } + } + } + + if (error) { throw new LogoError(Logo.messages.getString("erreur_stop")); } + if (marqueur + 2 > instruction.getLength()) + instruction = new InstructionBuffer(" "); + else + instruction.delete(0, marqueur + 2); + if (!caractere.equals("\n")) + { + Primitive.stackLoop.pop(); + } + return (caractere); + } + + /** + * This method deletes all instruction since it encounters the end of a + * procedure + * + * @param app + * The running frame Application + * @return an integer that indicates the number of loop to delete from + * Primitive.stackLoop + * @throws LogoError + */ + private void eraseLevelReturn(Application app) throws LogoError + { + boolean error = true; + String caractere = ""; + int loopLevel = 0; + int marqueur = 0; + InstructionBuffer instruction = app.getKernel().getInstructionBuffer(); + for (int i = 0; i < instruction.getLength(); i++) + { + caractere = String.valueOf(instruction.charAt(i)); + if (caractere.equals(Primitive.END_PROCEDURE)) + { + marqueur = i; + error = false; + break; + } + else if (caractere.equals(Primitive.END_LOOP)) + { + /* + * On test si le caractère "\" est bien un caractère de fin + * de boucle et non du style "\e" ou "\#" + */ + if (instruction.charAt(i + 1) == ' ') + { + loopLevel++; + } + } + } + if (error) { throw new LogoError(Logo.messages.getString("erreur_retourne")); } + if (marqueur + 2 > instruction.getLength()) + instruction = new InstructionBuffer(" "); + else + instruction.delete(0, marqueur + 2); + for (int i = 0; i < loopLevel; i++) + { + Primitive.stackLoop.pop(); + } + } + + private void exportPrimCSV() + { + StringBuffer sb = new StringBuffer(); + Locale locale = new Locale("fr", "FR"); + ResourceBundle prim_fr = ResourceBundle.getBundle("primitives", locale); + locale = new Locale("en", "US"); + ResourceBundle prim_en = ResourceBundle.getBundle("primitives", locale); + locale = new Locale("ar", "MA"); + ResourceBundle prim_ar = ResourceBundle.getBundle("primitives", locale); + locale = new Locale("es", "ES"); + ResourceBundle prim_es = ResourceBundle.getBundle("primitives", locale); + locale = new Locale("pt", "BR"); + ResourceBundle prim_pt = ResourceBundle.getBundle("primitives", locale); + locale = new Locale("eo", "EO"); + ResourceBundle prim_eo = ResourceBundle.getBundle("primitives", locale); + locale = new Locale("de", "DE"); + ResourceBundle prim_de = ResourceBundle.getBundle("primitives", locale); + // locale=new Locale("nl","NL"); + ResourceBundle[] prim = { prim_fr, prim_en, prim_es, prim_pt, prim_ar, prim_eo, prim_de }; + try + { + BufferedReader bfr = new BufferedReader(new InputStreamReader( + Primitive.class.getResourceAsStream("genericPrimitive"))); + int i = 0; + while (bfr.ready()) + { + String line = bfr.readLine(); + StringTokenizer cut = new StringTokenizer(line); + String cle = cut.nextToken(); + parametres[i] = Integer.parseInt(cut.nextToken()); + if (i != 39 && i != 95 && i != 212 && cle.length() != 1) + { + sb.append("$"); + sb.append(cle); + sb.append("$"); + sb.append(";"); + for (int j = 0; j < prim.length; j++) + { + String txt = cle; + txt = prim[j].getString(cle); + sb.append("$"); + sb.append(txt); + sb.append("$"); + sb.append(";"); + } + sb.append("\n"); + } + i++; + } + Utils.writeLogoFile("/home/loic/primTable.csv", new String(sb)); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + private void exportMessageCSV() + { + StringBuffer sb = new StringBuffer(); + Locale locale = new Locale("fr", "FR"); + ResourceBundle lang_fr = ResourceBundle.getBundle("langage", locale); + locale = new Locale("en", "US"); + ResourceBundle lang_en = ResourceBundle.getBundle("langage", locale); + locale = new Locale("ar", "MA"); + ResourceBundle lang_ar = ResourceBundle.getBundle("langage", locale); + locale = new Locale("es", "ES"); + ResourceBundle lang_es = ResourceBundle.getBundle("langage", locale); + locale = new Locale("pt", "BR"); + ResourceBundle lang_pt = ResourceBundle.getBundle("langage", locale); + locale = new Locale("eo", "EO"); + ResourceBundle lang_eo = ResourceBundle.getBundle("langage", locale); + locale = new Locale("de", "DE"); + ResourceBundle lang_de = ResourceBundle.getBundle("langage", locale); + // locale=new Locale("nl","NL"); + ResourceBundle[] lang = { lang_fr, lang_en, lang_es, lang_pt, lang_ar, lang_eo, lang_de }; + try + { + Enumeration<String> en = lang_fr.getKeys(); + while (en.hasMoreElements()) + { + String cle = en.nextElement(); + sb.append("$"); + sb.append(cle); + sb.append("$"); + sb.append(";"); + for (int j = 0; j < lang.length; j++) + { + String txt = lang[j].getString(cle); + sb.append("$"); + sb.append(txt); + sb.append("$"); + if (j != lang.length - 1) + sb.append(";"); + } + + sb.append("\n"); + } + Utils.writeLogoFile("/home/loic/messageTable.csv", new String(sb)); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + public static int isPrimitive(String s) + { + String index = Primitive.primitives.get(s.toLowerCase()); + if (null == index) + return -1; + try + { + int id = Integer.parseInt(index); + return id % Primitive.PRIMITIVE_NUMBER; + } + catch (NumberFormatException e) + { + System.err.println("!!!!!!!!!!!!!!!!!!!!!!!!!!\n" + + "The file xlogo/kernel/genericPrimitive has been corrupted...\n" + + " Please, check the integrity of xlogo.jar \n!!!!!!!!!!!!!!!!!!!!!!!!!"); + } + return -1; + } + +} diff --git a/logo/src/xlogo/kernel/Turtle.java b/logo/src/xlogo/kernel/Turtle.java new file mode 100644 index 0000000..82f3a76 --- /dev/null +++ b/logo/src/xlogo/kernel/Turtle.java @@ -0,0 +1,434 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +/** + * Title : XLogo + * Description : XLogo is an interpreter for the Logo + * programming language + * + * @author Loïc Le Coq + */ + +package xlogo.kernel; + +import java.awt.Toolkit; +import java.awt.MediaTracker; +import java.awt.geom.GeneralPath; +import java.awt.Color; +import java.awt.Image; +import java.awt.BasicStroke; +import java.awt.Stroke; +import java.util.StringTokenizer; + +import xlogo.Logo; +import xlogo.storage.WSManager; +import xlogo.storage.user.PenShape; +import xlogo.storage.user.UserConfig; +import xlogo.utils.Utils; +import xlogo.Application; + +public class Turtle +{ + + private Application app; + + public Color couleurcrayon = Color.black; + public Stroke stroke = new BasicStroke(1); + + Color couleurmodedessin = Color.black; + + public int id = -1; + + BasicStroke crayon = null; + + int police = 12; + + private int labelHorizontalAlignment = 0; + protected static final int LABEL_HORIZONTAL_ALIGNMENT_LEFT = 0; + protected static final int LABEL_HORIZONTAL_ALIGNMENT_CENTER = 1; + protected static final int LABEL_HORIZONTAL_ALIGNMENT_RIGHT = 2; + + private int labelVerticalAlignment = 0; + protected static final int LABEL_VERTICAL_ALIGNMENT_BOTTOM = 0; + protected static final int LABEL_VERTICAL_ALIGNMENT_CENTER = 1; + protected static final int LABEL_VERTICAL_ALIGNMENT_TOP = 2; + + // Image for the turtle + // If null then draw the triangle + + Image tort = null; + + GeneralPath triangle; + /** + * The turtle heading (degree) + */ + public double heading; + /** + * The turtle roll (degree) + */ + public double roll; + /** + * The turtle pitch (degree) + */ + public double pitch; + + /** + * The X coordinates on the screen + */ + public double corX; + /** + * The Y coordinates on the screen + */ + public double corY; + + public double angle; + /** + * The X coordinates in real World (3D or 2D) + */ + public double X = 0; + /** + * The Y coordinates in real World (3D or 2D) + */ + public double Y = 0; + /** + * The Z coordinates in real World (3D or 2D) + */ + public double Z = 0; + /** + * Identity Matrix + */ + private final double[][] identity = new double[3][3]; + { + identity[0][0] = identity[1][1] = identity[2][2] = 1; + identity[0][1] = identity[0][2] = identity[1][2] = 0; + identity[1][0] = identity[2][1] = identity[2][0] = 0; + } + /** + * This is the rotation Matrix (3x3) in 3D world + */ + private double[][] rotationMatrix = identity; + + int largeur = 0; + + int hauteur = 0; + + int gabarit = 0; + + private boolean pendown = true; + + private boolean penReverse = false; + + private boolean visible = true; + + private int shape = WSManager.getUserConfig().getActiveTurtle(); + + private float penWidth = 0; // half + // of + // the + // pen + // width + + public Turtle(Application app) + { + UserConfig uc = WSManager.getUserConfig(); + + this.app = app; + fixe_taille_crayon(1); + String chemin = "tortue" + uc.getActiveTurtle() + ".png"; + couleurcrayon = uc.getPencolor(); + couleurmodedessin = uc.getPencolor(); + if (uc.getActiveTurtle() == 0) + { + tort = null; + largeur = 26; + hauteur = 26; + } + else + { + // ON teste tout d'abord si le chemin est valide + if (null == Utils.class.getResource(chemin)) + chemin = "tortue1.png"; + tort = Toolkit.getDefaultToolkit().getImage(Utils.class.getResource(chemin)); + MediaTracker tracker = new MediaTracker(app.getFrame()); + tracker.addImage(tort, 0); + try + { + tracker.waitForID(0); + } + catch (InterruptedException e1) + {} + largeur = tort.getWidth(app.getFrame()); + hauteur = tort.getHeight(app.getFrame()); + double largeur_ecran = Toolkit.getDefaultToolkit().getScreenSize().getWidth(); + // On fait attention à la résolution de l'utilisateur + double facteur = largeur_ecran / 1024.0; + if ((int) (facteur + 0.001) != 1) + { + tort = tort.getScaledInstance((int) (facteur * largeur), (int) (facteur * hauteur), Image.SCALE_SMOOTH); + tracker = new MediaTracker(app.getFrame()); + tracker.addImage(tort, 0); + try + { + tracker.waitForID(0); + } + catch (InterruptedException e1) + {} + } + largeur = tort.getWidth(app.getFrame()); + hauteur = tort.getHeight(app.getFrame()); + } + gabarit = Math.max(hauteur, largeur); + corX = uc.getImageWidth() / 2; + corY = uc.getImageHeight() / 2; + angle = Math.PI / 2; + heading = 0.0; + pitch = 0; + roll = 0; + X = 0; + Y = 0; + Z = 0; + } + + protected void init() + { + UserConfig uc = WSManager.getUserConfig(); + + corX = uc.getImageWidth() / 2; + corY = uc.getImageHeight() / 2; + X = 0; + Y = 0; + Z = 0; + heading = 0; + pitch = 0; + roll = 0; + rotationMatrix = identity; + angle = Math.PI / 2; + pendown = true; + stroke = new BasicStroke(1); + fixe_taille_crayon(1); + couleurcrayon = uc.getPencolor(); + couleurmodedessin = uc.getPencolor(); + penReverse = false; + } + + void drawTriangle() + { + if (null == tort) + { + if (null == triangle) + { + triangle = new GeneralPath(); + } + else + triangle.reset(); + if (DrawPanel.WINDOW_MODE != DrawPanel.WINDOW_3D) + { + triangle.moveTo((float) (corX - 10.0 * Math.sin(angle)), (float) (corY - 10.0 * Math.cos(angle))); + triangle.lineTo((float) (corX + 24.0 * Math.cos(angle)), (float) (corY - 24.0 * Math.sin(angle))); + triangle.lineTo((float) (corX + 10.0 * Math.sin(angle)), (float) (corY + 10.0 * Math.cos(angle))); + triangle.lineTo((float) (corX - 10.0 * Math.sin(angle)), (float) (corY - 10.0 * Math.cos(angle))); + } + else + { + double[] screenCoord = new double[2]; + // The triangle has coordinates: (-10,0,0);(0,24,0);(10,0,0) + double[] x1 = new double[3]; + x1[0] = X - 20 * rotationMatrix[0][0]; + x1[1] = Y - 20 * rotationMatrix[1][0]; + x1[2] = Z - 20 * rotationMatrix[2][0]; + screenCoord = app.getDrawPanel().toScreenCoord(x1, false); + triangle.moveTo((float) screenCoord[0], (float) screenCoord[1]); + x1[0] = X + 48 * rotationMatrix[0][1]; + x1[1] = Y + 48 * rotationMatrix[1][1]; + x1[2] = Z + 48 * rotationMatrix[2][1]; + screenCoord = app.getDrawPanel().toScreenCoord(x1, false); + triangle.lineTo((float) screenCoord[0], (float) screenCoord[1]); + x1[0] = X + 20 * rotationMatrix[0][0]; + x1[1] = Y + 20 * rotationMatrix[1][0]; + x1[2] = Z + 20 * rotationMatrix[2][0]; + screenCoord = app.getDrawPanel().toScreenCoord(x1, false); + triangle.lineTo((float) screenCoord[0], (float) screenCoord[1]); + triangle.closePath(); + + // the "aileron" has coordinates: (0,10,0);(0,0,10);(0,0,0) + x1[0] = X + 15 * rotationMatrix[0][1]; + x1[1] = Y + 15 * rotationMatrix[1][1]; + x1[2] = Z + 15 * rotationMatrix[2][1]; + screenCoord = app.getDrawPanel().toScreenCoord(x1, false); + triangle.moveTo((float) screenCoord[0], (float) screenCoord[1]); + x1[0] = X + 15 * rotationMatrix[0][2]; + x1[1] = Y + 15 * rotationMatrix[1][2]; + x1[2] = Z + 15 * rotationMatrix[2][2]; + screenCoord = app.getDrawPanel().toScreenCoord(x1, false); + triangle.lineTo((float) screenCoord[0], (float) screenCoord[1]); + x1[0] = X; + x1[1] = Y; + x1[2] = Z; + screenCoord = app.getDrawPanel().toScreenCoord(x1, false); + triangle.lineTo((float) screenCoord[0], (float) screenCoord[1]); + } + } + } + + public void fixe_taille_crayon(float nb) + { + UserConfig uc = WSManager.getUserConfig(); + + if (nb < 0) + nb = 1; + else if (uc.getMaxPenWidth() != -1 && nb > uc.getMaxPenWidth()) + nb = 1; + if (uc.getPenShape() == PenShape.SQUARE) + crayon = new BasicStroke(nb, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER); + else + crayon = new BasicStroke(nb, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER); + penWidth = nb / 2; + } + + public float getPenWidth() + { + return penWidth; + } + + protected int getShape() + { + return shape; + } + + public void setShape(int id) + { + shape = id; + } + + protected boolean isVisible() + { + return visible; + } + + protected void setVisible(boolean b) + { + visible = b; + } + + protected boolean isPenDown() + { + return pendown; + } + + protected void setPenDown(boolean b) + { + pendown = b; + } + + protected boolean isPenReverse() + { + return penReverse; + } + + protected void setPenReverse(boolean b) + { + penReverse = b; + } + + protected void setRotationMatrix(double[][] m) + { + rotationMatrix = m; + } + + protected double[][] getRotationMatrix() + { + return rotationMatrix; + } + + protected double getX() + { + if (DrawPanel.WINDOW_MODE == DrawPanel.WINDOW_3D) + return X; + return corX - WSManager.getUserConfig().getImageWidth() / 2; + + } + + protected double getY() + { + if (DrawPanel.WINDOW_MODE == DrawPanel.WINDOW_3D) + return Y; + return WSManager.getUserConfig().getImageHeight() / 2 - corY; + + } + + protected int getLabelHorizontalAlignment() + { + return labelHorizontalAlignment; + } + + protected int getLabelVerticalAlignment() + { + return labelVerticalAlignment; + } + + protected void setFontJustify(String list) throws LogoError + { + StringTokenizer st = new StringTokenizer(list); + int i = 0; + while (st.hasMoreTokens()) + { + String s = st.nextToken(); + try + { + int j = Integer.parseInt(s); + if (j < 0 || j > 2) + throw new LogoError(list + " " + Logo.messages.getString("pas_argument")); + else + { + if (i == 0) + labelHorizontalAlignment = j; + else if (i == 1) + labelVerticalAlignment = j; + } + } + catch (NumberFormatException e) + { + throw new LogoError(list + " " + Logo.messages.getString("pas_argument")); + } + + i++; + } + if (i != 2) + throw new LogoError(list + " " + Logo.messages.getString("pas_argument")); + } + + public String getFontJustify() + { + StringBuffer sb = new StringBuffer("[ "); + sb.append(labelHorizontalAlignment); + sb.append(" "); + sb.append(labelVerticalAlignment); + sb.append(" ] "); + return new String(sb); + } +} diff --git a/logo/src/xlogo/kernel/genericPrimitive b/logo/src/xlogo/kernel/genericPrimitive new file mode 100644 index 0000000..bec63b7 --- /dev/null +++ b/logo/src/xlogo/kernel/genericPrimitive @@ -0,0 +1,308 @@ +drawing.av 1 +drawing.re 1 +drawing.td 1 +drawing.tg 1 +arithmetic.puissance 2 +controls.repete 2 +ve 0 +ct 0 +mt 0 +ec 1 + +si 2 +controls.stop 0 +drawing.origine 0 +drawing.fpos 1 +drawing.fixex 1 +drawing.fixey 1 +drawing.fixexy 2 +drawing.fixecap 1 +lc 0 +bc 0 +go 0 +ic 0 +de 0 +arithmetic.somme 2 + +arithmetic.difference 2 +arithmetic.moins 1 +arithmetic.produit 2 + +arithmetic.div 2 +arithmetic.reste 2 +ret 1 +* 1 +/ 1 ++ 1 +- 1 += 1 +< 1 +> 1 +| 1 +& 1 +\n 0 +\ 0 +pos 0 +cap 0 +arithmetic.arrondi 1 +arithmetic.log10 1 +arithmetic.sin 1 +arithmetic.cos 1 +ou 2 + +et 2 + +non 1 +liste 2 + +ph 2 + +mp 2 +md 2 +inverse 1 +choix 1 +enleve 2 +item 2 +sd 1 +sp 1 +dernier 1 +premier 1 +compte 1 +mot? 1 +nombre? 1 +liste? 1 +vide? 1 +egal? 2 +precede? 2 +membre? 2 +arithmetic.racine 1 +membre 2 +workspace.donne 2 +workspace.locale 1 +workspace.donnelocale 2 +fcc 1 +fcfg 1 +arithmetic.hasard 1 +attends 1 +workspace.imts 0 +workspace.efn 1 +workspace.efv 1 +workspace.efns 0 +mot 2 + +drawing.etiquette 1 +tc 1 +fen 0 +enr 0 +clos 0 +vt 0 +ci 1 +ftc 1 +controls.tantque 2 +lis 2 +touche? 0 +\siwhile 2 +liscar 0 +remplis 0 +drawing.point 1 +vers 1 +distance 1 +cc 0 +cf 0 +bc? 0 +visible? 0 +prim? 1 +proc? 1 +workspace.exec 1 +cat 0 +frep 1 +rep 0 +sauve 2 +sauved 1 +ramene 1 +arithmetic.pi 0 +arithmetic.tan 1 +arithmetic.acos 1 +arithmetic.asin 1 +arithmetic.atan 1 +vrai 0 +faux 0 +turtle.forme 0 +turtle.fforme 1 +workspace.def 2 +tortue 0 +tortues 0 +ftortue 1 +police 0 +fpolice 1 +tuetortue 1 +seq 1 +instr 0 +finstr 1 +joue 0 +efseq 0 +indseq 0 +findseq 1 +fpt 1 +ptexte 0 +fct 1 +couleurtexte 0 +lissouris 0 +possouris 0 +message 1 +date 0 +heure 0 +temps 0 +debuttemps 1 +fintemps? 0 +fixenompolice 1 +nompolice 0 +fixenompolicetexte 1 +nompolicetexte 0 +listeflux 0 +lisligneflux 1 +liscarflux 1 +ecrisligneflux 2 +finflux? 1 +ouvreflux 2 +fermeflux 1 +ajouteligneflux 2 +souris? 0 +workspace.listevariables 0 +workspace.chose 1 +nettoie 0 +tape 1 +drawing.cercle 1 +drawing.arc 3 +rempliszone 0 +animation 0 +rafraichis 0 +tailleimage 0 +arithmetic.quotient 2 +entier? 1 +fixeseparation 1 +separation 0 +arithmetic.tronque 1 +workspace.trace 0 +changedossier 1 +unicode 1 +caractere 1 +controls.stoptout 0 +controls.compteur 0 +controls.repetepour 2 +arithmetic.absolue 1 +remplace 3 +ajoute 3 +color.gris 0 +color.grisclair 0 +color.rougefonce 0 +color.vertfonce 0 +color.bleufonce 0 +color.orange 0 +color.rose 0 +color.violet 0 +color.marron 0 +color.noir 0 +color.rouge 0 +color.vert 0 +color.jaune 0 +color.bleu 0 +color.magenta 0 +color.cyan 0 +color.blanc 0 +) 0 +fixestyle 1 +style 0 +taillefenetre 0 +drawing.longueuretiquette 1 +envoietcp 2 +ecoutetcp 0 +executetcp 2 +\x 0 +chattcp 2 +resetall 0 +penwidth 0 +setpenshape 1 +penshape 0 +setdrawingquality 1 +drawingquality 0 +setturtlesnumber 1 +turtlesnumber 0 +setscreensize 1 +guibutton 2 +guiaction 2 +guiremove 1 +guiposition 2 +guidraw 1 +setzoom 1 +grille 2 +stopgrille 0 +stopanimation 0 +workspace.stoptrace 0 +guimenu 2 +axis 1 +xaxis 1 +yaxis 1 +stopaxis 0 +bye 0 +var? 1 +axiscolor 0 +gridcolor 0 +grid? 0 +xaxis? 0 +yaxis? 0 +setgridcolor 1 +setaxiscolor 1 +3d.perspective 0 +3d.rightroll 1 +3d.uppitch 1 +3d.leftroll 1 +3d.downpitch 1 +3d.roll 0 +3d.pitch 0 +3d.setroll 1 +3d.setpitch 1 +3d.setorientation 1 +3d.orientation 0 +3d.setxyz 3 +3d.setz 1 +workspace.pprop 3 +workspace.gprop 2 +workspace.remprop 2 +workspace.plist 1 +3d.polystart 0 +3d.polyend 0 +3d.polyview 0 +3d.linestart 0 +3d.lineend 0 +3d.pointstart 0 +3d.pointend 0 +3d.textstart 0 +3d.textend 0 +<= 1 +>= 1 +workspace.primitives 0 +workspace.propertylists 0 +workspace.content 0 +workspace.erpl 1 +arithmetic.exp 1 +arithmetic.log 1 +controls.ifelse 3 +workspace.ed 1 +workspace.edall 0 +controls.foreach 3 +controls.forever 1 +arithmetic.setdigits 1 +arithmetic.digits 0 +workspace.text 1 +workspace.externalcommand 1 +drawing.saveimage 2 +sound.mp3play 1 +sound.mp3stop 0 +zoom 0 +drawing.x 0 +drawing.y 0 +drawing.z 0 +drawing.fillpolygon 1 +arithmetic.alea 0 +loop.dountil 2 +loop.dowhile 2 +aritmetic.modulo 2 +drawing.setfontjustify 1 +drawing.fontjustify 0 +arithmetic.inf 2 +arithmetic.sup 2 +arithmetic.infequal 2 +arithmetic.supequal 2
\ No newline at end of file diff --git a/logo/src/xlogo/kernel/grammar/LogoException.java b/logo/src/xlogo/kernel/grammar/LogoException.java new file mode 100644 index 0000000..225a677 --- /dev/null +++ b/logo/src/xlogo/kernel/grammar/LogoException.java @@ -0,0 +1,69 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +/** + * Title : XLogo + * Description : XLogo is an interpreter for the Logo + * programming language + * Licence : GPL + * + * @author Loïc Le Coq + */ +package xlogo.kernel.grammar; + +/** + * @author loic + * + */ +public class LogoException extends LogoType +{ + + private String message; + + public LogoException(String message) + { + this.message = message; + } + + public boolean isException() + { + return true; + } + + public String toString() + { + return message; + } + + @Override + public String toDebug() + { + return "(EXCEPTION) " + message; + } + +} diff --git a/logo/src/xlogo/kernel/grammar/LogoList.java b/logo/src/xlogo/kernel/grammar/LogoList.java new file mode 100644 index 0000000..1cd6ea5 --- /dev/null +++ b/logo/src/xlogo/kernel/grammar/LogoList.java @@ -0,0 +1,93 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +/** + * Title : XLogo + * Description : XLogo is an interpreter for the Logo + * programming language + * Licence : GPL + * + * @author Loïc Le Coq + */ +package xlogo.kernel.grammar; + +import java.util.Vector; + +public class LogoList extends LogoType +{ + private Vector<LogoType> vector; + + LogoList(Vector<LogoType> vector) + { + this.vector = vector; + } + + LogoList() + { + vector = new Vector<LogoType>(); + } + + public boolean isList() + { + return true; + } + + public void add(LogoType type) + { + vector.add(type); + } + + public Vector<LogoType> getVector() + { + return vector; + } + + public String toString() + { + StringBuffer sb = new StringBuffer(); + sb.append("[ "); + for (int i = 0; i < vector.size(); i++) + { + sb.append(vector.get(i).toString()); + sb.append(" "); + } + sb.append("]"); + return sb.toString(); + } + + @Override + public String toDebug() + { + StringBuffer sb = new StringBuffer(); + sb.append("(LIST) "); + sb.append(toString()); + return sb.toString(); + + } + +} diff --git a/logo/src/xlogo/kernel/grammar/LogoNumber.java b/logo/src/xlogo/kernel/grammar/LogoNumber.java new file mode 100644 index 0000000..fd367bb --- /dev/null +++ b/logo/src/xlogo/kernel/grammar/LogoNumber.java @@ -0,0 +1,76 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +/** + * Title : XLogo + * Description : XLogo is an interpreter for the Logo + * programming language + * Licence : GPL + * + * @author Loïc Le Coq + */ +package xlogo.kernel.grammar; + +/** + * @author loic + * + */ +public class LogoNumber extends LogoType +{ + private double value; + + LogoNumber(double value) + { + this.value = value; + } + public double getValue() + { + return value; + } + + public boolean isWord() + { + return true; + } + + public boolean isNumber() + { + return true; + } + + public String toString() + { + return String.valueOf(value); + } + + @Override + public String toDebug() + { + return "(NUMBER) " + String.valueOf(value); + } +} diff --git a/logo/src/xlogo/kernel/grammar/LogoParser.java b/logo/src/xlogo/kernel/grammar/LogoParser.java new file mode 100644 index 0000000..ccf3436 --- /dev/null +++ b/logo/src/xlogo/kernel/grammar/LogoParser.java @@ -0,0 +1,289 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +/** + * Title : XLogo + * Description : XLogo is an interpreter for the Logo + * programming language + * + * @author Loïc Le Coq + */ + +package xlogo.kernel.grammar; + +import xlogo.Logo; +import xlogo.kernel.DrawPanel; +import xlogo.kernel.Primitive; +import xlogo.storage.global.GlobalConfig; + +public class LogoParser +{ + private char c; + private int cursor; + private String text; + + /** + * + * @param sr + * The String input reader + */ + public LogoParser(String text) + { + this.text = text; + cursor = 0; + while (cursor < text.length()) + { + LogoType lt = getToken(); + if (GlobalConfig.DEBUG) + System.out.println("[DEBUG] Token " + lt.toDebug() + " cursor " + cursor); + } + } + + private LogoType getToken() + { + boolean isQuotedWord = false; + boolean isVariable = false; + + StringBuffer sb = new StringBuffer(); + boolean start = false; + boolean backslash = false; + do + { + c = text.charAt(cursor); + // Skip White Spaces + if (c == ' ' || c == '\t') + { + if (start) + break; + else + cursor++; + } + else + { + if (backslash) + { + if (c == ' ') + sb.append(" "); + else if (c == '#') + sb.append("#"); + else if (c == '\\') + sb.append("\\"); + else if (c == '(') + sb.append("("); + else if (c == ')') + sb.append(")"); + else if (c == '[') + sb.append("["); + else if (c == ']') + sb.append("]"); + else if (c == 'n') + sb.append("\n"); + else + sb.append(c); + cursor++; + } + else + { + // If it's the first character, check for type + if (!start) + { + if (c == ':') + isVariable = true; + else if (c == '\"') + isQuotedWord = true; + } + if (c == '\\') + backslash = true; + + else if (c == '[') + { + if (start) + break; + else + { + cursor++; + return extractList(); + } + } + else if (c == '(') + { + if (start) + break; + } + else if (c == '*' || c == '/' || c == '+' || c == '-' || c == '|' || c == '&' || c == '=') + { + if (!isQuotedWord) + { + if (!start) + { + sb.append(c); + cursor++; + } + break; + + } + else + cursor++; + } + else if (c == ')') + return new LogoException(Logo.messages.getString("parenthese_ouvrante")); + else if (c == ']') + return new LogoException(Logo.messages.getString("error.whattodo") + " ]"); + else + { + sb.append(c); + cursor++; + } + } + start = true; + } + } while (cursor < text.length()); + if (sb.length() == 0) + return DrawPanel.nullType; + else if (isQuotedWord) + return new LogoWord(sb.substring(1)); + else if (isVariable) + return new LogoVariable(sb.substring(1)); + try + { + double d = Double.parseDouble(sb.toString()); + return new LogoNumber(d); + } + catch (NumberFormatException e) + { + int id = Primitive.isPrimitive(sb.toString()); + if (id != -1) { return new LogoPrimitive(id, sb.toString()); } + return new LogoException(Logo.messages.getString("je_ne_sais_pas") + " " + sb.toString()); + } + + } + + /** + * This method extracts a list. + * + * @return a LogoList if operation succeed, + * a LogoException otherwise + */ + + private LogoType extractList() + { + LogoList list = new LogoList(); + while (cursor < text.length()) + { + LogoType lt = getListToken(); + if (lt.isNull()) + { + return new LogoException(Logo.messages.getString("erreur_crochet")); + } + else if (lt.isRightDelimiter()) + return list; + else + { + list.add(lt); + } + } + return new LogoException(Logo.messages.getString("erreur_crochet")); + } + + private LogoType getListToken() + { + StringBuffer sb = new StringBuffer(); + boolean start = false; + boolean backslash = false; + for (int i = cursor; i < text.length(); i++) + { + cursor = i; + c = text.charAt(i); + // Skip White Spaces + if (c == ' ' || c == '\t') + { + if (start) + break; + } + else + { + if (backslash) + { + if (c == ' ') + sb.append(" "); + else if (c == '#') + sb.append("#"); + else if (c == '\\') + sb.append("\\"); + else if (c == '(') + sb.append("("); + else if (c == ')') + sb.append(")"); + else if (c == '[') + sb.append("["); + else if (c == ']') + sb.append("]"); + else if (c == 'n') + sb.append("\n"); + else + sb.append(c); + } + else + { + if (c == '\\') + { + backslash = true; + } + else if (c == '[') + { + if (start) + break; + else + { + cursor++; + return extractList(); + } + } + else if (c == ']') + { + if (start) + break; + else + { + System.out.println("coucou"); + cursor++; + return new LogoRightDelimiter(); + } + } + else + sb.append(c); + } + start = true; + } + } + if (sb.length() == 0) + return DrawPanel.nullType; + return new LogoWord(sb.toString()); + } + +} diff --git a/logo/src/xlogo/kernel/grammar/LogoPrimitive.java b/logo/src/xlogo/kernel/grammar/LogoPrimitive.java new file mode 100644 index 0000000..52044a3 --- /dev/null +++ b/logo/src/xlogo/kernel/grammar/LogoPrimitive.java @@ -0,0 +1,74 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +/** + * Title : XLogo + * Description : XLogo is an interpreter for the Logo + * programming language + * Licence : GPL + * + * @author Loïc Le Coq + */ +package xlogo.kernel.grammar; + +public class LogoPrimitive extends LogoType +{ + private int id; + private String name; + + LogoPrimitive(int id, String name) + { + this.id = id; + this.name = name; + } + + public boolean isPrimitive() + { + return true; + } + + /** + * @return + * @uml.property name="id" + */ + public int getId() + { + return id; + } + + public String toString() + { + return name; + } + + @Override + public String toDebug() + { + return "(PRIMITIVE id=" + id + ") " + name; + } +} diff --git a/logo/src/xlogo/kernel/grammar/LogoRightDelimiter.java b/logo/src/xlogo/kernel/grammar/LogoRightDelimiter.java new file mode 100644 index 0000000..fb46026 --- /dev/null +++ b/logo/src/xlogo/kernel/grammar/LogoRightDelimiter.java @@ -0,0 +1,56 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +/** + * Title : XLogo + * Description : XLogo is an interpreter for the Logo + * programming language + * Licence : GPL + * + * @author Loïc Le Coq + */ +package xlogo.kernel.grammar; + +/** + * @author loic + * + */ +public class LogoRightDelimiter extends LogoType +{ + public boolean isRightDelimiter() + { + return true; + } + + @Override + public String toDebug() + { + return null; + } + +} diff --git a/logo/src/xlogo/kernel/grammar/LogoTree.java b/logo/src/xlogo/kernel/grammar/LogoTree.java new file mode 100644 index 0000000..4e6b66e --- /dev/null +++ b/logo/src/xlogo/kernel/grammar/LogoTree.java @@ -0,0 +1,96 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +package xlogo.kernel.grammar; + +import java.util.Vector; + +public class LogoTree +{ + private Vector<LogoTree> children; + private LogoTree parent; + private LogoType value; + private boolean isRoot = false; + private boolean isProcedure = false; + private boolean isPrimitive = false; + private boolean isLeaf = false; + + LogoTree() + { + children = new Vector<LogoTree>(); + } + + protected void setParent(LogoTree lt) + { + this.parent = lt; + } + + protected LogoTree getParent() + { + return parent; + } + + protected boolean isRoot() + { + return isRoot; + } + + protected void addChild(LogoTree child) + { + children.add(child); + } + + protected void setValue(LogoType value) + { + this.value = value; + } + + protected LogoType getValue() + { + return value; + } + + protected boolean isLeaf() + { + return isLeaf; + } + + LogoType evaluate() + { + Vector<LogoType> args = new Vector<LogoType>(); + for (int i = 0; i < children.size(); i++) + { + LogoTree child = children.get(i); + if (child.isLeaf()) + args.add(child.getValue()); + else + args.add(child.evaluate()); + } + return null; + } +} diff --git a/logo/src/xlogo/kernel/grammar/LogoType.java b/logo/src/xlogo/kernel/grammar/LogoType.java new file mode 100644 index 0000000..d407431 --- /dev/null +++ b/logo/src/xlogo/kernel/grammar/LogoType.java @@ -0,0 +1,129 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +/** + * Title : XLogo + * Description : XLogo is an interpreter for the Logo + * programming language + * Licence : GPL + * + * @author Loïc Le Coq + */ + +package xlogo.kernel.grammar; + +public abstract class LogoType +{ + + /** + * If this token is a word ? + * + * @return true for a word, false otherwise + */ + public boolean isWord() + { + return false; + } + + /** + * If this token is a list? + * + * @return true for a list, false otherwise + */ + public boolean isList() + { + return false; + } + + /** + * If this token is a number? + * + * @return true for a number, false otherwise + */ + public boolean isNumber() + { + return false; + } + + /** + * If this token is a variable? + * + * @return true for a variable, false otherwise + */ + public boolean isVariable() + { + return false; + } + + /** + * If this token is a primitive? + * + * @return true for a primitive, false otherwise + */ + public boolean isPrimitive() + { + return false; + } + + /** + * If this token is a procedure? + * + * @return true for a procedure, false otherwise + */ + public boolean isProcedure() + { + return false; + } + + /** + * If this token is an exception? + * + * @return true for an exception, false otherwise + */ + public boolean isException() + { + return false; + } + + public boolean isNull() + { + return false; + } + + public boolean isRightDelimiter() + { + return false; + } + + /** + * Util for debugging + * + * @return the type and value for LogoType + */ + abstract public String toDebug(); +} diff --git a/logo/src/xlogo/kernel/grammar/LogoTypeNull.java b/logo/src/xlogo/kernel/grammar/LogoTypeNull.java new file mode 100644 index 0000000..b764063 --- /dev/null +++ b/logo/src/xlogo/kernel/grammar/LogoTypeNull.java @@ -0,0 +1,56 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +/** + * Title : XLogo + * Description : XLogo is an interpreter for the Logo + * programming language + * Licence : GPL + * + * @author Loïc Le Coq + */ +package xlogo.kernel.grammar; + +/** + * @author loic + * + */ +public class LogoTypeNull extends LogoType +{ + public boolean isNull() + { + return true; + } + + @Override + public String toDebug() + { + return "(LogoTypeNull)"; + } + +} diff --git a/logo/src/xlogo/kernel/grammar/LogoVariable.java b/logo/src/xlogo/kernel/grammar/LogoVariable.java new file mode 100644 index 0000000..d2a7e91 --- /dev/null +++ b/logo/src/xlogo/kernel/grammar/LogoVariable.java @@ -0,0 +1,68 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +package xlogo.kernel.grammar; + +/** + * Title : XLogo + * Description : XLogo is an interpreter for the Logo + * programming language + * Licence : GPL + * + * @author Loïc Le Coq + */ +public class LogoVariable extends LogoType +{ + String name; + + LogoVariable(String name) + { + this.name = name; + } + + public boolean isVariable() + { + return true; + } + + public String getName() + { + return name; + } + + public String toString() + { + return ":" + name; + } + + @Override + public String toDebug() + { + return "(VARIABLE) :" + name; + } +} diff --git a/logo/src/xlogo/kernel/grammar/LogoWord.java b/logo/src/xlogo/kernel/grammar/LogoWord.java new file mode 100644 index 0000000..e155180 --- /dev/null +++ b/logo/src/xlogo/kernel/grammar/LogoWord.java @@ -0,0 +1,68 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +/** + * Title : XLogo + * Description : XLogo is an interpreter for the Logo + * programming language + * Licence : GPL + * + * @author Loïc Le Coq + */ +package xlogo.kernel.grammar; + +public class LogoWord extends LogoType +{ + private String value; + + LogoWord(String value) + { + this.value = value; + } + + public String getQuotedValue() + { + return "\"" + value; + } + + public String toString() + { + return value; + } + + public boolean isWord() + { + return true; + } + + @Override + public String toDebug() + { + return "(WORD) " + value; + } +} diff --git a/logo/src/xlogo/kernel/gui/GuiButton.java b/logo/src/xlogo/kernel/gui/GuiButton.java new file mode 100644 index 0000000..1d440a5 --- /dev/null +++ b/logo/src/xlogo/kernel/gui/GuiButton.java @@ -0,0 +1,92 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +package xlogo.kernel.gui; + +import javax.swing.JButton; + +import xlogo.kernel.Interprete; + +import java.awt.Font; +import java.awt.event.*; + +import xlogo.Application; +import xlogo.storage.WSManager; +import xlogo.utils.Utils; + +public class GuiButton extends GuiComponent +{ + + private StringBuffer action; + + private Application app; + + public GuiButton(String id, String text, Application app) + { + Font font = WSManager.getWorkspaceConfig().getFont(); + super.setId(id); + guiObject = new JButton(Utils.SortieTexte(text)); + this.app = app; + java.awt.FontMetrics fm = app.getFrame().getGraphics().getFontMetrics(font); + originalWidth = fm.stringWidth(((JButton) (getGuiObject())).getText()) + 50; + originalHeight = font.getSize() + 10; + setSize(originalWidth, originalHeight); + } + + public void actionPerformed(ActionEvent e) + { + if (!app.commande_isEditable()) + { + Interprete.actionInstruction.append(action); + } + else + { + app.startInterpretation(action); + } + } + + public boolean isButton() + { + return true; + } + + public boolean isMenu() + { + return false; + } + + /** + * @param action + * @uml.property name="action" + */ + public void setAction(StringBuffer action) + { + this.action = action; + } + +} diff --git a/logo/src/xlogo/kernel/gui/GuiComponent.java b/logo/src/xlogo/kernel/gui/GuiComponent.java new file mode 100644 index 0000000..c144432 --- /dev/null +++ b/logo/src/xlogo/kernel/gui/GuiComponent.java @@ -0,0 +1,95 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +package xlogo.kernel.gui; + +import java.awt.event.ActionEvent; +import java.awt.Point; +import java.awt.event.ActionListener; +import javax.swing.JComponent; + +public abstract class GuiComponent implements ActionListener +{ + public boolean hasAction = false; + protected int originalWidth; + protected int originalHeight; + private String id; + protected JComponent guiObject; + protected boolean actionFinished; + + public String getId() + { + return id; + } + + protected void setId(String id) + { + this.id = id; + } + + public boolean hasAction() + { + return hasAction; + } + + public JComponent getGuiObject() + { + return guiObject; + } + + public void setLocation(int x, int y) + { + guiObject.setLocation(x, y); + } + + public Point getLocation() + { + return guiObject.getLocation(); + } + + public void setSize(int width, int height) + { + guiObject.setSize(width, height); + } + + public int getOriginalWidth() + { + return originalWidth; + } + + public int getOriginalHeight() + { + return originalHeight; + } + + public abstract void actionPerformed(ActionEvent e); + + public abstract boolean isButton(); + + public abstract boolean isMenu(); +} diff --git a/logo/src/xlogo/kernel/gui/GuiMap.java b/logo/src/xlogo/kernel/gui/GuiMap.java new file mode 100644 index 0000000..d2c071e --- /dev/null +++ b/logo/src/xlogo/kernel/gui/GuiMap.java @@ -0,0 +1,72 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +package xlogo.kernel.gui; + +import xlogo.kernel.LogoError; + +import java.util.HashMap; + +import xlogo.Logo; + +public class GuiMap extends HashMap<String, GuiComponent> +{ + private static final long serialVersionUID = 1L; + + public GuiMap() + { + } + + public void put(GuiComponent gc) throws LogoError + { + if (this.containsKey(gc.getId())) + { + throw new LogoError(Logo.messages.getString("gui_exists") + " " + gc.getId()); + } + else + this.put(gc.getId(), gc); + } + + public GuiComponent get(Object key) + { + String k = key.toString().toLowerCase(); + return super.get(k); + } + + /* + * public void remove(GuiComponent gc) throws myException{ + * if (this.containsKey(gc.getId())){ + * this.remove(gc.getId()); + * } + * else{ + * throw new + * myException(app,Logo.messages.getString("no_gui")+" "+gc.getId()); + * } + * } + */ +} diff --git a/logo/src/xlogo/kernel/gui/GuiMenu.java b/logo/src/xlogo/kernel/gui/GuiMenu.java new file mode 100644 index 0000000..2a424f3 --- /dev/null +++ b/logo/src/xlogo/kernel/gui/GuiMenu.java @@ -0,0 +1,101 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +package xlogo.kernel.gui; + +import javax.swing.JComboBox; + +import xlogo.storage.WSManager; +import xlogo.utils.Utils; + +import java.util.StringTokenizer; + +import xlogo.kernel.Interprete; + +import java.awt.event.*; + +import xlogo.Application; + +public class GuiMenu extends GuiComponent +{ + private Application app; + private String[] item; + private StringBuffer[] action; + + public GuiMenu(String id, String text, Application app) + { + this.app = app; + setId(id); + StringTokenizer st = new StringTokenizer(text); + item = new String[st.countTokens()]; + action = new StringBuffer[st.countTokens()]; + int i = 0; + originalWidth = 0; + while (st.hasMoreTokens()) + { + item[i] = Utils.SortieTexte(st.nextToken()); + java.awt.FontMetrics fm = app.getFrame().getGraphics() + .getFontMetrics(WSManager.getWorkspaceConfig().getFont()); + originalWidth = Math.max(originalWidth, fm.stringWidth(item[i])); + i++; + } + originalWidth += 50; + guiObject = new JComboBox(item); + originalHeight = WSManager.getWorkspaceConfig().getFont().getSize() + 10; + setSize(originalWidth, originalHeight); + } + + public void actionPerformed(ActionEvent e) + { + // System.out.println("coucou"); + int select = ((JComboBox) guiObject).getSelectedIndex(); + if (!app.commande_isEditable()) + { + Interprete.actionInstruction.append(action[select]); + } + else + { + app.startInterpretation(action[select]); + } + } + + public boolean isButton() + { + return false; + } + + public boolean isMenu() + { + return true; + } + + public void setAction(StringBuffer action, int id) + { + this.action[id] = action; + } +} diff --git a/logo/src/xlogo/kernel/network/ChatFrame.java b/logo/src/xlogo/kernel/network/ChatFrame.java new file mode 100644 index 0000000..227f4ca --- /dev/null +++ b/logo/src/xlogo/kernel/network/ChatFrame.java @@ -0,0 +1,160 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +package xlogo.kernel.network; + +import java.awt.Color; +import java.awt.Font; +import java.awt.Toolkit; + +import javax.swing.BorderFactory; +import javax.swing.JFrame; +import javax.swing.JScrollPane; +import javax.swing.JTextPane; +import javax.swing.JTextField; + +import java.awt.event.*; +import java.awt.Dimension; +import java.io.PrintWriter; + +import javax.swing.text.BadLocationException; +import javax.swing.text.DefaultStyledDocument; +import javax.swing.text.MutableAttributeSet; +import javax.swing.text.SimpleAttributeSet; +import javax.swing.text.StyleConstants; + +import java.awt.BorderLayout; + +import xlogo.Application; +import xlogo.storage.WSManager; +import xlogo.utils.Utils; +import xlogo.Logo; + +public class ChatFrame extends JFrame implements ActionListener +{ + private static final long serialVersionUID = 1L; + + private MutableAttributeSet local; + + private MutableAttributeSet distant; + + private JTextPane textPane; + + private DefaultStyledDocument dsd; + + private JScrollPane scroll; + + private JTextField textField; + + private Application app; + + private PrintWriter out; + + protected ChatFrame(PrintWriter out, Application app) + { + this.app = app; + this.out = out; + initStyle(); + initGui(); + textField.addActionListener(this); + } + + private void initStyle() + { + Font font = WSManager.getWorkspaceConfig().getFont(); + local = new SimpleAttributeSet(); + StyleConstants.setFontFamily(local, font.getFamily()); + StyleConstants.setForeground(local, Color.black); + StyleConstants.setFontSize(local, font.getSize()); + + distant = new SimpleAttributeSet(); + StyleConstants.setFontFamily(distant, font.getFamily()); + StyleConstants.setForeground(distant, Color.RED); + StyleConstants.setFontSize(distant, font.getSize()); + } + + private void initGui() + { + setTitle(Logo.messages.getString("chat")); + setIconImage(Toolkit.getDefaultToolkit().createImage(Utils.class.getResource("icone.png"))); + dsd = new DefaultStyledDocument(); + textPane = new JTextPane(); + textPane.setDocument(dsd); + textPane.setEditable(false); + textPane.setBackground(new Color(255, 255, 220)); + this.getContentPane().setLayout(new BorderLayout()); + textField = new JTextField(); + java.awt.FontMetrics fm = app.getFrame().getGraphics().getFontMetrics(WSManager.getWorkspaceConfig().getFont()); + int width = fm.stringWidth(Logo.messages.getString("stop_chat")) + 30; + if (width < 200) + width = 200; + + textPane.setPreferredSize(new Dimension(width, 300)); + textField.setPreferredSize(new Dimension(width, WSManager.getWorkspaceConfig().getFont().getSize() + 10)); + scroll = new JScrollPane(textPane); + getContentPane().add(scroll, BorderLayout.CENTER); + getContentPane().add(textField, BorderLayout.SOUTH); + textPane.setBorder(BorderFactory.createLineBorder(Color.BLUE, 3)); + textField.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + pack(); + setVisible(true); + } + + protected void append(String sty, String st) + { + st += "\n"; + try + { + if (sty.equals("local")) + dsd.insertString(dsd.getLength(), st, local); + if (sty.equals("distant")) + dsd.insertString(dsd.getLength(), ">" + st, distant); + textPane.setCaretPosition(dsd.getLength()); + + } + catch (BadLocationException e) + {} + } + + protected void processWindowEvent(WindowEvent e) + { + if (e.getID() == WindowEvent.WINDOW_CLOSING) + { + out.println(NetworkServer.END_OF_FILE); + dispose(); + } + } + + public void actionPerformed(ActionEvent e) + { + String txt = textField.getText(); + append("local", txt); + textField.setText(""); + out.println(txt); + } +} diff --git a/logo/src/xlogo/kernel/network/NetworkClientChat.java b/logo/src/xlogo/kernel/network/NetworkClientChat.java new file mode 100644 index 0000000..cf9ce0b --- /dev/null +++ b/logo/src/xlogo/kernel/network/NetworkClientChat.java @@ -0,0 +1,115 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +package xlogo.kernel.network; + +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; + +import xlogo.Application; +import xlogo.Logo; +import xlogo.kernel.LogoError; +import xlogo.storage.WSManager; + +/** + * Title : XLogo + * Description : XLogo is an interpreter for the Logo + * programming language + * + * @author Loïc Le Coq + */ + +public class NetworkClientChat +{ + private InetAddress ip; + private String st; + private Application app; + private PrintWriter out; + private BufferedReader in; + private Socket socket; + ChatFrame cf; + + public NetworkClientChat(Application app, String ip, String st) throws LogoError + { + this.app = app; + try + { + this.ip = InetAddress.getByName(ip); + } + catch (UnknownHostException e) + { + throw new LogoError(Logo.messages.getString("no_host") + " " + ip); + } + this.st = st; + init(); + } + + private void init() throws LogoError + { + try + { + socket = new Socket(ip, WSManager.getUserConfig().getTcpPort()); + java.io.OutputStream os = socket.getOutputStream(); + BufferedOutputStream b = new BufferedOutputStream(os); + OutputStreamWriter osw = new OutputStreamWriter(b, "UTF8"); + out = new PrintWriter(osw, true); + in = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF8")); + String cmd = NetworkServer.CHATTCP + "\n"; + cmd += st + "\n"; + cmd += NetworkServer.END_OF_FILE; + cf = new ChatFrame(out, app); + cf.append("local", st); + out.println(cmd); + while ((cmd = in.readLine()) != null) + { + if (cmd.equals(NetworkServer.END_OF_FILE)) + { + cf.append("local", Logo.messages.getString("stop_chat")); + break; + } + cf.append("distant", cmd); + } + out.close(); + in.close(); + socket.close(); + } + catch (IOException e) + { + throw new LogoError(Logo.messages.getString("no_host") + ip.getHostAddress()); + } + + } + +} diff --git a/logo/src/xlogo/kernel/network/NetworkClientExecute.java b/logo/src/xlogo/kernel/network/NetworkClientExecute.java new file mode 100644 index 0000000..22c3753 --- /dev/null +++ b/logo/src/xlogo/kernel/network/NetworkClientExecute.java @@ -0,0 +1,114 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +package xlogo.kernel.network; + +/** + * Title : XLogo + * Description : XLogo is an interpreter for the Logo + * programming language + * + * @author Loïc Le Coq + */ + +import java.net.Socket; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.UnknownHostException; + +import xlogo.storage.WSManager; + +import java.net.InetAddress; + +import xlogo.Logo; +import xlogo.kernel.Kernel; +import xlogo.kernel.LogoError; + +public class NetworkClientExecute +{ + private InetAddress ip; + private String cmd; + private PrintWriter out; + private BufferedReader in; + private Socket socket; + private Kernel kernel; + + public NetworkClientExecute(Kernel kernel, String ip, String cmd) throws LogoError + { + this.kernel = kernel; + try + { + this.ip = InetAddress.getByName(ip); + } + catch (UnknownHostException e) + { + throw new LogoError(Logo.messages.getString("no_host") + " " + ip); + } + this.cmd = cmd; + init(); + } + + private void init() throws LogoError + { + try + { + socket = new Socket(ip, WSManager.getUserConfig().getTcpPort()); + java.io.OutputStream os = socket.getOutputStream(); + BufferedOutputStream b = new BufferedOutputStream(os); + OutputStreamWriter osw = new OutputStreamWriter(b, "UTF8"); + out = new PrintWriter(osw, true); + in = new BufferedReader(new InputStreamReader(socket.getInputStream())); + String wp = NetworkServer.EXECUTETCP + "\n"; + wp += kernel.getWorkspace().getSerializedContext(); // Marko : changed + wp += NetworkServer.END_OF_FILE; + out.println(wp); + String input; + while ((input = in.readLine()) != null) + { + // System.out.println("je vais envoyer: OK"); + if (input.equals("OK")) + { + // System.out.println("chargement reussi"); + break; + } + } + out.println(cmd); + out.close(); + in.close(); + socket.close(); + } + catch (IOException e) + { + throw new LogoError(Logo.messages.getString("no_host") + ip.getHostAddress()); + } + } +} diff --git a/logo/src/xlogo/kernel/network/NetworkClientSend.java b/logo/src/xlogo/kernel/network/NetworkClientSend.java new file mode 100644 index 0000000..2954436 --- /dev/null +++ b/logo/src/xlogo/kernel/network/NetworkClientSend.java @@ -0,0 +1,100 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +package xlogo.kernel.network; + +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; + +import xlogo.Logo; +import xlogo.kernel.LogoError; +import xlogo.storage.WSManager; + +public class NetworkClientSend +{ + + private InetAddress ip; + private String data; + private BufferedReader in; + private PrintWriter out; + private Socket socket; + private String answer; + + public NetworkClientSend(String ip, String data) throws LogoError + { + try + { + this.ip = InetAddress.getByName(ip); + } + catch (UnknownHostException e) + { + throw new LogoError(Logo.messages.getString("no_host") + " " + ip); + } + this.data = data; + init(); + } + + /** + * @return + * @uml.property name="answer" + */ + public String getAnswer() + { + return answer; + } + + private void init() throws LogoError + { + try + { + socket = new Socket(ip, WSManager.getUserConfig().getTcpPort()); + in = new BufferedReader(new InputStreamReader(socket.getInputStream())); + + java.io.OutputStream os = socket.getOutputStream(); + BufferedOutputStream b = new BufferedOutputStream(os); + OutputStreamWriter osw = new OutputStreamWriter(b, "UTF8"); + out = new PrintWriter(osw, true); + out.println(data); + answer = ""; + answer = in.readLine(); + out.close(); + socket.close(); + } + catch (IOException e) + { + throw new LogoError(Logo.messages.getString("no_host") + ip.getHostAddress()); + } + } +} diff --git a/logo/src/xlogo/kernel/network/NetworkServer.java b/logo/src/xlogo/kernel/network/NetworkServer.java new file mode 100644 index 0000000..f12b383 --- /dev/null +++ b/logo/src/xlogo/kernel/network/NetworkServer.java @@ -0,0 +1,194 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +package xlogo.kernel.network; + +/** + * Title : XLogo + * Description : XLogo is an interpreter for the Logo + * programming language + * + * @author Loïc Le Coq + */ +import java.net.ServerSocket; +import java.net.Socket; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.BufferedReader; +import java.io.InputStreamReader; + +import xlogo.storage.WSManager; +import xlogo.Application; +import xlogo.Logo; +import xlogo.kernel.LogoError; +import xlogo.kernel.Kernel; +import xlogo.messages.MessageKeys; +import xlogo.messages.async.history.HistoryMessenger; + +public class NetworkServer +{ + public static boolean isActive; + private ServerSocket serverSocket; + private Application app; + private PrintWriter out; + private BufferedReader in; + private ChatFrame cf; + + protected static final String EXECUTETCP = "executetcp" + "\u001B"; + protected static final String CHATTCP = "chattcp" + "\u001B"; + protected static final String END_OF_FILE = "*/EOF\\*" + "\u001B"; + private Kernel kernel; + + public NetworkServer(Application app) throws LogoError + { + isActive = true; + this.app = app; + this.kernel = app.getKernel(); + init(); + + } + + private void init() throws LogoError + { + try + { + serverSocket = new ServerSocket(WSManager.getUserConfig().getTcpPort()); + } + catch (IOException e) + { + throw (new LogoError(Logo.messages.getString("pb_port") + WSManager.getUserConfig().getTcpPort())); + } + Socket clientSocket = null; + try + { + clientSocket = serverSocket.accept(); + java.io.OutputStream os = clientSocket.getOutputStream(); + BufferedOutputStream b = new BufferedOutputStream(os); + OutputStreamWriter osw = new OutputStreamWriter(b, "UTF8"); + out = new PrintWriter(osw, true); + in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream(), "UTF8")); + String inputLine = ""; + inputLine = in.readLine(); + // ******************* Executetcp ************************** + if (inputLine.equals(NetworkServer.EXECUTETCP)) + { + StringBuffer remoteNetworkContext = new StringBuffer(); + while ((inputLine = in.readLine()) != null) + { + if (inputLine.equals(NetworkServer.END_OF_FILE)) + break; + remoteNetworkContext.append(inputLine); + remoteNetworkContext.append("\n"); + } + + /* + * Marko Zivkovic : New implementation of this context switch + */ + + try + { + kernel.getWorkspace().pushNetworkMode(remoteNetworkContext.toString()); + } + catch(Exception e) + { + throw new LogoError(Logo.messages.getString(MessageKeys.ERROR_NETWORK_CONTEXT)); + } + + // We say the workspace is fully created + out.println("OK"); + // What is the command to execute? + inputLine = in.readLine(); + // System.out.println("a exécuter: "+inputLine); + out.close(); + in.close(); + clientSocket.close(); + serverSocket.close(); + + kernel.execute(new StringBuffer(inputLine + "\\x ")); + + } + // ******************* Chattcp ************************** + else if (inputLine.equals(NetworkServer.CHATTCP)) + { + String sentence = ""; + while ((inputLine = in.readLine()) != null) + { + if (inputLine.equals(NetworkServer.END_OF_FILE)) + break; + sentence = inputLine; + } + cf = new ChatFrame(out, app); + cf.append("distant", sentence); + while ((sentence = in.readLine()) != null) + { + if (sentence.equals(NetworkServer.END_OF_FILE)) + { + cf.append("local", Logo.messages.getString("stop_chat")); + break; + } + cf.append("distant", sentence); + } + out.close(); + in.close(); + clientSocket.close(); + serverSocket.close(); + } + // ******************* Envoietcp ************************** + else + { + HistoryMessenger.getInstance().dispatchMessage(inputLine); + out.println(Logo.messages.getString("pref.ok")); + out.close(); + in.close(); + clientSocket.close(); + serverSocket.close(); + + } + } + catch (IOException e) + { + throw new LogoError(Logo.messages.getString("erreur_tcp")); + } + isActive = false; + } + + public static void stopServer() + { + String st = NetworkServer.EXECUTETCP + "\n" + NetworkServer.END_OF_FILE + "\n\n"; + try + { + Socket socket = new Socket("127.0.0.1", WSManager.getUserConfig().getTcpPort()); + PrintWriter out = new PrintWriter(socket.getOutputStream(), true); + out.println(st); + } + catch (IOException e) + {} + } +} diff --git a/logo/src/xlogo/kernel/perspective/Conic.java b/logo/src/xlogo/kernel/perspective/Conic.java new file mode 100644 index 0000000..6963b57 --- /dev/null +++ b/logo/src/xlogo/kernel/perspective/Conic.java @@ -0,0 +1,214 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +package xlogo.kernel.perspective; + +import java.math.BigDecimal; + +import xlogo.kernel.DrawPanel; + +/** + * @author Marko : This is now decoupled from Application + * + */ +public class Conic +{ + private DrawPanel dp; + BigDecimal a; + BigDecimal b; + BigDecimal c; + BigDecimal d; + BigDecimal e; + BigDecimal f; + BigDecimal deux = new BigDecimal(2); + private final BigDecimal un = BigDecimal.ONE; + private final BigDecimal zero = BigDecimal.ZERO; + BigDecimal[][] A = new BigDecimal[2][2]; + private BigDecimal[] eigenValue = new BigDecimal[2]; + private BigDecimal[][] eigenVect = new BigDecimal[2][2]; + private BigDecimal[] center = new BigDecimal[2]; + private double halfXAxis; + private double halfYAxis; + + public Conic(DrawPanel dp, BigDecimal[] v) + { + this.dp = dp; + this.a = v[0]; + this.b = v[1]; + this.c = v[2]; + this.d = v[3]; + this.e = v[4]; + this.f = v[5]; + A[0][0] = a; + A[1][1] = c; + // b/2 + A[0][1] = b.divide(deux); + A[1][0] = b.divide(deux); + drawConic(); + } + + private void drawConic() + { + calculateEigenVect(); + // ellipse + BigDecimal det = det(A); + if (det.compareTo(zero) == 1) + { + // Looking for center + calculateCenter(); + // The main equation becomes with the center as Origin: + // aX^2+bXY+cY^2+omega=0 + + // double omega=a*Math.pow(center[0],2)+c*Math.pow(center[1],2) + // +b*center[0]*center[1]+d*center[0]+e*center[1]+f; + + BigDecimal omega = a.multiply(center[0].pow(2)).add(c.multiply(center[1].pow(2))) + .add(center[0].multiply(center[1]).multiply(b)).add(d.multiply(center[0])) + .add(e.multiply(center[1])).add(f); + + // We apply the rotation with the Eigen Vect Matrix + // Now the equation becomes: lambda*X^2+mu*Y^2+omega=0 + // with lambda and mu the eigen Values + // 1/sqrt(eigenValue[0]/-omega) + halfXAxis = 1 / Math.sqrt(eigenValue[0].divide(omega.negate(), 20, BigDecimal.ROUND_HALF_EVEN) + .doubleValue()); + // 1/sqrt(eigenValue[1]/-omega) + halfYAxis = 1 / Math.sqrt(eigenValue[1].divide(omega.negate(), 20, BigDecimal.ROUND_HALF_EVEN) + .doubleValue()); + double angle = Math.atan(eigenVect[1][0].divide(eigenVect[0][0], 20, BigDecimal.ROUND_HALF_EVEN) + .doubleValue()); + // System.out.println(toString()); + dp.drawEllipseArc(halfXAxis, halfYAxis, angle, center[0].doubleValue(), + center[1].doubleValue(), 0, 360); + + } + // hyperbola + else if (det.compareTo(zero) == -1) + { + calculateCenter(); + + } + // parabola + else + {} + } + + private void calculateEigenValue() + { + // Polynom: acX^2-(a+c)X+A[0][1]^2 + // tmp=a+c + BigDecimal tmp = A[0][0].add(A[1][1]); + // Discriminant delta=(a-c)^2+4*A[0][1]^2 + BigDecimal delta = A[0][0].subtract(A[1][1]).pow(2).add(new BigDecimal(4).multiply(A[0][1].pow(2))); + // calculate the eigen Values + eigenValue[0] = tmp.subtract(sqrt(delta)).divide(deux); + eigenValue[1] = tmp.add(sqrt(delta)).divide(deux); + } + + private void calculateEigenVect() + { + if (A[0][1].compareTo(zero) == 0) + { + eigenValue[0] = A[0][0]; + eigenValue[1] = A[1][1]; + eigenVect[0][0] = un; + eigenVect[1][0] = zero; + eigenVect[0][1] = zero; + eigenVect[1][1] = un; + } + else + { + calculateEigenValue(); + BigDecimal tmp = A[0][0].subtract(eigenValue[0]); + eigenVect[0][0] = un; + // System.out.println("pb: "+tmp+"\n"+A[0][1]); + eigenVect[1][0] = tmp.negate().divide(A[0][1], 20, BigDecimal.ROUND_HALF_EVEN); + // vector length and then normalize + tmp = sqrt(eigenVect[0][0].pow(2).add(eigenVect[1][0].pow(2))); + eigenVect[0][0] = eigenVect[0][0].divide(tmp, 20, BigDecimal.ROUND_HALF_EVEN); + eigenVect[1][0] = eigenVect[1][0].divide(tmp, 20, BigDecimal.ROUND_HALF_EVEN); + eigenVect[0][1] = eigenVect[1][0].negate(); + eigenVect[1][1] = un; + + } + } + + private void calculateCenter() + { + // System determinant + // df/dx=0 and df/dy=0 + + // double det=4*a*c-Math.pow(b,2); + BigDecimal det = new BigDecimal(4).multiply(a).multiply(c).subtract(b.pow(2)); + // xCenter=(-2*c*d+b*e)/det; + center[0] = (deux.negate().multiply(c).multiply(d).add(b.multiply(e))).divide(det, 20, + BigDecimal.ROUND_HALF_EVEN); + // yCenter=(-2*a*e+b*d)/det; + center[1] = (deux.negate().multiply(a).multiply(e).add(b.multiply(d))).divide(det, 20, + BigDecimal.ROUND_HALF_EVEN); + } + + private BigDecimal det(BigDecimal[][] M) + { + return M[0][0].multiply(M[1][1]).subtract(M[0][1].multiply(M[1][0])); + } + + public String toString() + { + String st = "centre: " + center[0] + " " + center[1] + "\n" + "Valeurs propres: " + eigenValue[0] + " " + + eigenValue[1] + "\n" + "Vecteurs propres:\n" + eigenVect[0][0] + " " + eigenVect[0][1] + "\n" + + eigenVect[1][0] + " " + eigenVect[1][1] + "\n" + "Demi Axe: X " + halfXAxis + " Y " + halfYAxis; + return st; + } + + private BigDecimal sqrt(BigDecimal b) + { + BigDecimal precision = new BigDecimal(0.00000000001); + int scale = precision.scale(); + if (b.scale() < scale) + { + b = b.setScale(scale); + } + // Initial guess + // G = A/2 + BigDecimal g = new BigDecimal(Math.sqrt(b.doubleValue())); + + // Iterate until we're done + while (true) + { + // G* = ((A/G)+G)/2 + BigDecimal gStar = b.divide(g, BigDecimal.ROUND_HALF_EVEN).add(g).divide(deux, BigDecimal.ROUND_HALF_EVEN); + BigDecimal delta = gStar.subtract(g); + delta = delta.abs(); + if (delta.compareTo(precision) < 0) + break; + g = gStar; + } + return g; + } +} diff --git a/logo/src/xlogo/kernel/perspective/Element3D.java b/logo/src/xlogo/kernel/perspective/Element3D.java new file mode 100644 index 0000000..d3473cf --- /dev/null +++ b/logo/src/xlogo/kernel/perspective/Element3D.java @@ -0,0 +1,115 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +package xlogo.kernel.perspective; + +import java.awt.Color; +import java.util.Vector; + +import javax.vecmath.Point3d; + +import xlogo.kernel.LogoError; + +/** + * @author Marko - I decoupled this and its subclasses from Application + * + */ +abstract public class Element3D +{ + protected Viewer3D v3d; + + /** + * Color for each vertex + * + * @uml.property name="color" + * @uml.associationEnd multiplicity="(0 -1)" elementType="java.awt.Color" + */ + protected Vector<Color> color; + + /** + * Vertex coordinates in virtual world + * + * @uml.property name="vertex" + * @uml.associationEnd multiplicity="(0 -1)" + * elementType="javax.vecmath.Point3d" + */ + protected Vector<Point3d> vertex; + + public Element3D(Viewer3D v3d) + { + this.v3d = v3d; + vertex = new Vector<Point3d>(); + color = new Vector<Color>(); + + } + + public void addVertex(Point3d p, Color c) + { + vertex.add(p); + color.add(c); + } + + /** + * This method calculates all attributes for polygon and add it in the 3D + * Viewer + */ + abstract public void addToScene() throws LogoError; + + /** + * This method indicates if the Element3D is a Point. + * + * @return true if this Element3D is a Point, false otherwise + */ + abstract public boolean isPoint(); + + /** + * This method indicates if the Element3D is a Polygon. + * + * @return true if this Element3D is a Polygon, false otherwise + */ + abstract public boolean isPolygon(); + + /** + * This method indicates if the Element3D is a line. + * + * @return true if this Element3D is a line, false otherwise + */ + abstract public boolean isLine(); + + /** + * This method indicates if the Element3D is a Text2D. + * + * @return true if this Element3D is a Text2D, false otherwise + */ + abstract public boolean isText(); + + public int getVertexCount() + { + return vertex.size(); + } +} diff --git a/logo/src/xlogo/kernel/perspective/ElementLine.java b/logo/src/xlogo/kernel/perspective/ElementLine.java new file mode 100644 index 0000000..5aa0a8a --- /dev/null +++ b/logo/src/xlogo/kernel/perspective/ElementLine.java @@ -0,0 +1,374 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +package xlogo.kernel.perspective; + +import javax.media.j3d.Appearance; +import javax.media.j3d.LineStripArray; +import javax.media.j3d.Material; +import javax.media.j3d.PolygonAttributes; +import javax.media.j3d.Shape3D; +import javax.media.j3d.LineAttributes; +import javax.media.j3d.TransformGroup; + +import com.sun.j3d.utils.geometry.Sphere; + +import javax.media.j3d.Transform3D; +import javax.vecmath.Color3f; +import javax.vecmath.Point3d; +import javax.vecmath.AxisAngle4d; + +import com.sun.j3d.utils.geometry.Cylinder; + +import javax.vecmath.Vector3d; + +import xlogo.kernel.LogoError; + +/** + * + * @author Marko Zivkovic - I decoupled this from Application + * + */ +public class ElementLine extends Element3D +{ + /** + * This float stores the line Width for each 3D line + * + * @uml.property name="lineWidth" + */ + private float lineWidth; + + public ElementLine(Viewer3D v3d, float lineWidth) + { + super(v3d); + this.lineWidth = lineWidth; //app.getKernel().getActiveTurtle().getPenWidth(); + } + + public void addToScene() throws LogoError + { + int size = vertex.size(); + if (size > 1) + { + if (lineWidth == 0.5) + createSimpleLine(size); + else + createComplexLine(size); + } + } + + /** + * This method draws a line with width 1 + * + * @param size + */ + private void createSimpleLine(int size) + { + int[] tab = { size }; + LineStripArray line = new LineStripArray(size, LineStripArray.COORDINATES | LineStripArray.COLOR_3, tab); + for (int i = 0; i < size; i++) + { + line.setCoordinate(i, vertex.get(i)); + // System.out.println("sommet "+(2*i-1)+" "+vertex.get(i).x+" "+vertex.get(i).y+" "+vertex.get(i).z+" "); + line.setColor(i, new Color3f(color.get(i))); + } + Shape3D s = new Shape3D(line); + Appearance appear = new Appearance(); + Material mat = new Material(new Color3f(1.0f, 1.0f, 1.0f), new Color3f(0.0f, 0f, 0f), new Color3f(1f, 1.0f, + 1.0f), new Color3f(1f, 1f, 1f), 64); + appear.setMaterial(mat); + appear.setLineAttributes(new LineAttributes(2 * lineWidth, LineAttributes.PATTERN_SOLID, false)); + s.setAppearance(appear); + // DrawPanel.listPoly.add(s); + v3d.add3DObject(s); + /* + * for (int i=0;i<line.getVertexCount();i++){ + * double[] d=new double[3]; + * line.getCoordinate(i, d); + * for(int j=0;j<d.length;j++){ + * System.out.println(i+" "+d[j]); + * } + * } + */ + } + + /** + * This method draws a sequence of lines with a width different from 1 + * Draws a cylinder around the line + * + * @param size + */ + + private void createComplexLine(int size) + { + for (int i = 0; i < size - 1; i++) + { + createLine(vertex.get(i), vertex.get(i + 1), new Color3f(color.get(i + 1))); + } + + } + + /** + * This method creates an elementary line with a width different from 1 + * It draws a cylinder around the line and two spheres on each extremities + * + * @param p1 + * the first point + * @param p2 + * the second point + * @param color + * the sphere color + */ + private void createLine(Point3d p1, Point3d p2, Color3f color) + { + // create a new CylinderTransformer between the two atoms + CylinderTransformer cT = new CylinderTransformer(p1, p2); + + // get the length + double length = cT.getLength(); + float radius = lineWidth / 1000; + // holds the translation + Transform3D transform1 = new Transform3D(); + // ...move the coordinates there + transform1.setTranslation(cT.getTranslation()); + + // get the axis and angle for rotation + AxisAngle4d rotation = cT.getAxisAngle(); + Transform3D transform2 = new Transform3D(); + transform2.setRotation(rotation); + + // combine the translation and rotation into transform1 + transform1.mul(transform2); + + // create a new Cylinder + Appearance appear = new Appearance(); + Material mat = new Material(new Color3f(1.0f, 1.0f, 1.0f), color, new Color3f(1f, 1.0f, 1.0f), new Color3f(1f, + 1f, 1f), 64); + appear.setMaterial(mat); + PolygonAttributes pa = new PolygonAttributes(); + pa.setCullFace(PolygonAttributes.CULL_NONE); + pa.setBackFaceNormalFlip(true); + appear.setPolygonAttributes(pa); + Cylinder cyl = new Cylinder(radius, (float) length, appear); + + // and add it to the TransformGroup + TransformGroup tg = new TransformGroup(); + tg.setTransform(transform1); + tg.addChild(cyl); + v3d.add2DText(tg); + + // Add Sphere to the line extremities. + Sphere sphere1 = new Sphere(radius, appear); + TransformGroup tg2 = new TransformGroup(); + Transform3D transform3 = new Transform3D(); + transform3.setTranslation(new Vector3d(p1)); + tg2.setTransform(transform3); + tg2.addChild(sphere1); + v3d.add2DText(tg2); + + Sphere sphere2 = new Sphere(radius, appear); + TransformGroup tg3 = new TransformGroup(); + Transform3D transform4 = new Transform3D(); + transform4.setTranslation(new Vector3d(p2)); + tg3.setTransform(transform4); + tg3.addChild(sphere2); + v3d.add2DText(tg3); + + } + + public boolean isLine() + { + return true; + } + + public boolean isPoint() + { + return false; + } + + public boolean isPolygon() + { + return false; + } + + public boolean isText() + { + return false; + } + + /** + * A class to calculate the length and transformations necessary to produce + * a cylinder to connect two points. Useful for Java3D and VRML where a + * cylinder object is created aligned along the y-axis. + * + * @author Alastair Hill + */ + class CylinderTransformer + { + + /** point A */ + private final Point3d pointA; + /** point B */ + private final Point3d pointB; + /** + * the angle through which to rotate the cylinder + */ + private double angle; + /** + * the axis around which to rotate the cylinder + * + */ + private Vector3d axis; + /** + * The translation required to translate the cylinder to the midpoint of + * the two points + */ + private Vector3d translation; + /** + * The length of the cylinder required to join the two points + * + */ + private double length; + + /** + * Creates a new instance of CylinderTransformer + * + * @param a + * point a + * @param b + * point b + */ + CylinderTransformer(Point3d a, Point3d b) + { + pointA = a; + pointB = b; + + // carry out the calculations + doCalculations(); + } + + /** + * Carries out the necessary calculations so that values may be returned + */ + private void doCalculations() + { + length = pointA.distance(pointB); + + double[] arrayA = new double[3]; + pointA.get(arrayA); + double[] arrayB = new double[3]; + pointB.get(arrayB); + double[] arrayMid = new double[3]; + + for (int i = 0; i < arrayA.length; i++) + { + arrayMid[i] = (arrayA[i] + arrayB[i]) / 2f; + } + + // the translation needed is + translation = new Vector3d(arrayMid); + + // the initial orientation of the bond is in the y axis + Vector3d init = new Vector3d(0.0f, 1.0f, 0.0f); + + // the vector needed is the same as that from a to b + Vector3d needed = new Vector3d(pointB.x - pointA.x, pointB.y - pointA.y, pointB.z - pointA.z); + + // so the angle to rotate the bond by is: + angle = needed.angle(init); + + // and the axis to rotate by is the cross product of the initial and + // needed vectors - ie the vector orthogonal to them both + axis = new Vector3d(); + axis.cross(init, needed); + } + + /** + * Returns the angle (in radians) through which to rotate the cylinder + * + * @return the angle. + */ + double getAngle() + { + return angle; + } + + /** + * The axis around which the cylinder must be rotated + * + * @return the axis + */ + Vector3d getAxis() + { + return axis; + } + + /** + * The length required for the cylinder to join the two points + * + * @return the length + */ + double getLength() + { + return length; + } + + /** + * The translation required to move the cylinder to the midpoint of the + * two points + * + * @return the translation + */ + Vector3d getTranslation() + { + return translation; + } + + /** + * Generates a (pretty) string representation of the the + * CylinderTransformer + * + * @return the string representation + */ + public String toString() + { + return "tr: " + translation + ", ax: " + axis + ", an: " + angle + ", le: " + length; + } + + /** + * Generates the required axis and angle combined into an AxisAngle4f + * object + * + * @return the axis and angle + */ + public AxisAngle4d getAxisAngle() + { + return new AxisAngle4d(axis.x, axis.y, axis.z, angle); + } + } + +} diff --git a/logo/src/xlogo/kernel/perspective/ElementPoint.java b/logo/src/xlogo/kernel/perspective/ElementPoint.java new file mode 100644 index 0000000..c77011a --- /dev/null +++ b/logo/src/xlogo/kernel/perspective/ElementPoint.java @@ -0,0 +1,129 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +package xlogo.kernel.perspective; + +import javax.media.j3d.Appearance; +import javax.media.j3d.Material; +import javax.media.j3d.PointArray; +import javax.media.j3d.PolygonAttributes; +import javax.media.j3d.Transform3D; +import javax.media.j3d.TransformGroup; +import javax.vecmath.Point3d; +import javax.vecmath.Color3f; +import javax.vecmath.Vector3d; + +import xlogo.kernel.LogoError; + +import javax.media.j3d.Shape3D; + +import com.sun.j3d.utils.geometry.Sphere; + +/** + * @author Marko - I decoupled this from Application + */ +public class ElementPoint extends Element3D +{ + float pointWidth; + + public ElementPoint(Viewer3D v3d, float pointWidth) + { + super(v3d); + this.pointWidth = pointWidth; //app.getKernel().getActiveTurtle().getPenWidth(); + } + + public void addToScene() throws LogoError + { + if (vertex.size() == 0) + return; + if (pointWidth == 0.5) + { + int[] tab = new int[1]; + tab[0] = vertex.size(); + PointArray point = new PointArray(vertex.size(), PointArray.COORDINATES | PointArray.COLOR_3); + for (int i = 0; i < vertex.size(); i++) + { + point.setCoordinate(i, vertex.get(i)); + point.setColor(i, new Color3f(color.get(i))); + } + v3d.add3DObject(new Shape3D(point)); + } + else + { + for (int i = 0; i < vertex.size(); i++) + { + createBigPoint(vertex.get(i), new Color3f(color.get(i))); + } + + } + } + + private void createBigPoint(Point3d p, Color3f color) + { + // Add a Sphere to main 3D scene. + Appearance appear = new Appearance(); + Material mat = new Material(new Color3f(1.0f, 1.0f, 1.0f), color,// new + // Color3f(0.0f,0f,0f), + new Color3f(1f, 1.0f, 1.0f), new Color3f(1f, 1f, 1f), 64); + appear.setMaterial(mat); + PolygonAttributes pa = new PolygonAttributes(); + pa.setCullFace(PolygonAttributes.CULL_NONE); + pa.setBackFaceNormalFlip(true); + appear.setPolygonAttributes(pa); + + Sphere sphere = new Sphere(pointWidth / 1000, appear); + + TransformGroup tg = new TransformGroup(); + Transform3D transform = new Transform3D(); + transform.setTranslation(new Vector3d(p)); + tg.setTransform(transform); + tg.addChild(sphere); + v3d.add2DText(tg); + } + + public boolean isLine() + { + return false; + } + + public boolean isPoint() + { + return true; + } + + public boolean isPolygon() + { + return false; + } + + public boolean isText() + { + return false; + } + +} diff --git a/logo/src/xlogo/kernel/perspective/ElementPolygon.java b/logo/src/xlogo/kernel/perspective/ElementPolygon.java new file mode 100644 index 0000000..d0aa85b --- /dev/null +++ b/logo/src/xlogo/kernel/perspective/ElementPolygon.java @@ -0,0 +1,179 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +/** + * Title : XLogo + * Description : XLogo is an interpreter for the Logo + * programming language + * + * @author Loïc Le Coq + */ +package xlogo.kernel.perspective; + +import javax.media.j3d.Appearance; +import javax.media.j3d.Material; +import javax.media.j3d.PolygonAttributes; +import javax.media.j3d.TriangleFanArray; +import javax.vecmath.Point3d; +import javax.media.j3d.Shape3D; +import javax.vecmath.Vector3f; +import javax.vecmath.Color3f; + +import xlogo.Logo; +import xlogo.kernel.LogoError; + +/** + * This class represent A polygon surface in 3D mode + * + * @author loic + * + * @author Marko Zivkovic - I decoupled this from Application + */ +public class ElementPolygon extends Element3D +{ + public ElementPolygon(Viewer3D v3d) + { + super(v3d); + } + + /** + * This method calculates all attributes for polygon and add it to the + * Polygon's list + */ + public void addToScene() throws LogoError + { + + if (vertex.size() < 3) + throw new LogoError(Logo.messages.getString("error.3d.3vertex")); + + // Create normal vector + + Point3d origine = vertex.get(0); + // System.out.println(" origine "+origine.x+" "+origine.y+" "+origine.z); + + Point3d point1; + Vector3f vec1 = null; + Vector3f vec2 = null; + for (int i = 1; i < vertex.size(); i++) + { + point1 = vertex.get(i); + if (!point1.equals(origine)) + { + // System.out.println(" point1 "+point1.x+" "+point1.y+" "+point1.z); + vec1 = new Vector3f((float) (point1.x - origine.x), (float) (point1.y - origine.y), + (float) (point1.z - origine.z)); + break; + } + } + if (null == vec1) + throw new LogoError(Logo.messages.getString("error.3d.emptypolygon")); + for (int i = 2; i < vertex.size(); i++) + { + point1 = vertex.get(i); + // System.out.println(" point1 "+point1.x+" "+point1.y+" "+point1.z); + vec2 = new Vector3f((float) (point1.x - origine.x), (float) (point1.y - origine.y), + (float) (point1.z - origine.z)); + if (vec1.dot(vec2) == 0) + vec2 = null; + else + { + // System.out.println(" vec1 "+vec1.x+" "+vec1.y+" "+vec1.z); + // System.out.println(" vec2 "+vec2.x+" "+vec2.y+" "+vec2.z); + vec2.cross(vec1, vec2); + vec2.normalize(); + vec1 = new Vector3f(vec2); + vec1.negate(); + // System.out.println("Après"+" vec1 "+vec1.x+" "+vec1.y+" "+vec1.z); + // System.out.println("Après"+" vec2 "+vec2.x+" "+vec2.y+" "+vec2.z); + // if (vec1.x!=0&& vec1.y!=0&& vec1.z!=0) + // System.out.println("coucou"+" vec1 "+vec1.x+" "+vec1.y+" "+vec1.z); + break; + } + if (null == vec2) + throw new LogoError(Logo.messages.getString("error.3d.emptypolygon")); + } + + // Create Geometry + + int[] tab = new int[1]; + tab[0] = vertex.size(); + TriangleFanArray tfa = new TriangleFanArray(vertex.size(), TriangleFanArray.COORDINATES + | TriangleFanArray.COLOR_3 | TriangleFanArray.NORMALS, tab); + // TriangleFanArray tfa2=new + // TriangleFanArray(vertex.size(),TriangleFanArray.COORDINATES|TriangleFanArray.COLOR_3|TriangleFanArray.NORMALS,tab); + for (int i = 0; i < vertex.size(); i++) + { + + tfa.setCoordinate(i, vertex.get(i)); + // tfa2.setCoordinate(i, vertex.get(vertex.size()-1-i)); + + tfa.setColor(i, new Color3f(color.get(i))); + // tfa2.setColor(i, new Color3f(color.get(color.size()-i-1))); + + tfa.setNormal(i, vec2); + // tfa2.setNormal(i, vec1); + + } + + Shape3D s = new Shape3D(tfa); + Appearance appear = new Appearance(); + Material mat = new Material(new Color3f(1.0f, 1.0f, 1.0f), new Color3f(0.0f, 0f, 0f), new Color3f(1f, 1.0f, + 1.0f), new Color3f(1f, 1f, 1f), 64); + appear.setMaterial(mat); + PolygonAttributes pa = new PolygonAttributes(); + pa.setCullFace(PolygonAttributes.CULL_NONE); + pa.setBackFaceNormalFlip(true); + appear.setPolygonAttributes(pa); + + s.setAppearance(appear); + v3d.add3DObject(s); + // DrawPanel.listPoly.add(s); + // DrawPanel.listPoly.add(new Shape3D(tfa2)); + // System.out.println(DrawPanel.listPoly.size()+" "+vertex.get(i).x+" "+vertex.get(i).y+" "+vertex.get(i).z); + } + + public boolean isPoint() + { + return false; + } + + public boolean isPolygon() + { + return true; + } + + public boolean isLine() + { + return false; + } + + public boolean isText() + { + return false; + } +} diff --git a/logo/src/xlogo/kernel/perspective/FogDialog.java b/logo/src/xlogo/kernel/perspective/FogDialog.java new file mode 100644 index 0000000..24b8bd1 --- /dev/null +++ b/logo/src/xlogo/kernel/perspective/FogDialog.java @@ -0,0 +1,256 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +package xlogo.kernel.perspective; + +import java.awt.Font; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; + +import xlogo.Logo; +import xlogo.storage.WSManager; + +public class FogDialog extends JDialog implements ActionListener +{ + private static final long serialVersionUID = 1L; + + private String[] type = { Logo.messages.getString("3d.fog.none"), + Logo.messages.getString("3d.fog.linear"), Logo.messages.getString("3d.fog.exponential") }; + + private JComboBox comboType; + + private PanelValue panelDensity; + + private PanelValue panelFront; + + private PanelValue panelBack; + + private JLabel labelType; + + private JButton ok; + + private JButton refresh; + + private Viewer3D viewer3d; + + private MyFog fog; + + FogDialog(Viewer3D viewer3d, MyFog fog, String title) + { + super(viewer3d, title, true); + this.viewer3d = viewer3d; + this.fog = fog; + initGui(); + } + + private void initGui() + { + getContentPane().setLayout(new GridBagLayout()); + setSize(450, 200); + labelType = new JLabel(Logo.messages.getString("3d.fog.type")); + comboType = new JComboBox(type); + comboType.setSelectedIndex(fog.getType()); + + panelDensity = new PanelValue(fog.getDensity(), Logo.messages.getString("3d.fog.density")); + panelFront = new PanelValue((int) (fog.getFront() * 1000), Logo.messages.getString("3d.fog.frontdistance")); + panelBack = new PanelValue((int) (fog.getBack() * 1000), Logo.messages.getString("3d.fog.backdistance")); + + Font font = WSManager.getWorkspaceConfig().getFont(); + + ok = new JButton(Logo.messages.getString("pref.ok")); + refresh = new JButton(Logo.messages.getString("3d.light.apply")); + labelType.setFont(font); + comboType.setFont(font); + ok.setFont(font); + refresh.setFont(font); + + comboType.addActionListener(this); + comboType.setActionCommand("combo"); + ok.addActionListener(this); + ok.setActionCommand("ok"); + refresh.addActionListener(this); + refresh.setActionCommand("refresh"); + + getContentPane().add( + labelType, + new GridBagConstraints(0, 0, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, + new Insets(0, 0, 0, 0), 0, 0)); + getContentPane().add( + comboType, + new GridBagConstraints(1, 0, 1, 1, 1.0, 1.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, + new Insets(0, 0, 0, 0), 0, 0)); + getContentPane().add( + panelFront, + new GridBagConstraints(0, 1, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, + new Insets(0, 0, 0, 0), 0, 0)); + getContentPane().add( + panelBack, + new GridBagConstraints(1, 1, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, + new Insets(0, 0, 0, 0), 0, 0)); + getContentPane().add( + panelDensity, + new GridBagConstraints(0, 2, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, + new Insets(0, 0, 0, 0), 0, 0)); + getContentPane().add( + refresh, + new GridBagConstraints(0, 4, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, + new Insets(0, 0, 0, 0), 0, 0)); + getContentPane().add( + ok, + new GridBagConstraints(1, 4, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, + new Insets(0, 0, 0, 0), 0, 0)); + selectComponents(); + setVisible(true); + + } + + public void actionPerformed(ActionEvent e) + { + String cmd = e.getActionCommand(); + // The selected item in the combo Box has changed + if (cmd.equals("combo")) + selectComponents(); + // Button Ok pressed + else if (cmd.equals("ok")) + { + updateFog(); + dispose(); + } + // Button Apply pressed + else if (cmd.equals("refresh")) + { + updateFog(); + } + } + + private void updateFog() + { + int t = comboType.getSelectedIndex(); + float d = panelDensity.getTextValue(); + float back = panelBack.getTextValue(); + float front = panelFront.getTextValue(); + fog.setType(t); + fog.setDensity(d); + fog.setBack(back / 1000); + fog.setFront(front / 1000); + fog.detach(); + fog.removeAllChildren(); + fog.createFog(); + viewer3d.addNode(fog); + } + + private void selectComponents() + { + int id = comboType.getSelectedIndex(); + // None + if (id == MyFog.FOG_OFF) + { + panelDensity.setEnabled(false); + panelBack.setEnabled(false); + panelFront.setEnabled(false); + } + // Linear Fog + else if (id == MyFog.FOG_LINEAR) + { + panelDensity.setEnabled(false); + panelBack.setEnabled(true); + panelFront.setEnabled(true); + } + // Exponential Fog + else if (id == MyFog.FOG_EXPONENTIAL) + { + panelBack.setEnabled(false); + panelFront.setEnabled(false); + panelDensity.setEnabled(true); + } + } + + class PanelValue extends JPanel + { + private static final long serialVersionUID = 1L; + private JLabel label; + private JTextField text; + private String title; + private float textValue; + + PanelValue(float textValue, String title) + { + this.textValue = textValue; + this.title = title; + initGui(); + } + + private void initGui() + { + label = new JLabel(title); + label.setFont(WSManager.getWorkspaceConfig().getFont()); + String st; + int i = (int) textValue; + if (i == textValue) + { + st = String.valueOf(i); + + } + else + st = String.valueOf(textValue); + text = new JTextField(st, 4); + add(label); + add(text); + } + + public void setEnabled(boolean b) + { + super.setEnabled(b); + label.setEnabled(b); + text.setEnabled(b); + } + + float getTextValue() + { + try + { + float f = Float.parseFloat(text.getText()); + return f; + } + catch (NumberFormatException e) + {} + return 0; + } + } + +} diff --git a/logo/src/xlogo/kernel/perspective/LightDialog.java b/logo/src/xlogo/kernel/perspective/LightDialog.java new file mode 100644 index 0000000..13aa671 --- /dev/null +++ b/logo/src/xlogo/kernel/perspective/LightDialog.java @@ -0,0 +1,378 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +package xlogo.kernel.perspective; + +import java.awt.Font; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.Color; + +import xlogo.Logo; + +import javax.vecmath.Color3f; +import javax.vecmath.Tuple3f; +import javax.vecmath.Point3f; +import javax.vecmath.Vector3f; +import javax.swing.BorderFactory; +import javax.swing.JDialog; +import javax.swing.JComboBox; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.JLabel; +import javax.swing.JButton; +import javax.swing.border.TitledBorder; + +import java.awt.event.*; + +import xlogo.gui.preferences.PanelColor; +import xlogo.storage.WSManager; + +public class LightDialog extends JDialog implements ActionListener +{ + private static final long serialVersionUID = 1L; + private String[] type = { Logo.messages.getString("3d.light.none"), + Logo.messages.getString("3d.light.ambient"), Logo.messages.getString("3d.light.directional"), + Logo.messages.getString("3d.light.point"), Logo.messages.getString("3d.light.spot") }; + + private JComboBox comboType; + + PanelColor panelColor; + + private PanelPosition panelPosition; + + private PanelPosition panelDirection; + + private PanelAngle panelAngle; + + private JLabel labelType; + + private JButton ok; + + private JButton refresh; + + private Viewer3D viewer3d; + + private MyLight light; + + LightDialog(Viewer3D viewer3d, MyLight light, String title) + { + super(viewer3d, title, true); + this.viewer3d = viewer3d; + this.light = light; + initGui(); + } + + private void initGui() + { + getContentPane().setLayout(new GridBagLayout()); + setSize(500, 200); + labelType = new JLabel(Logo.messages.getString("3d.light.type")); + comboType = new JComboBox(type); + comboType.setSelectedIndex(light.getType()); + + Color3f col = light.getColor(); + if (null != col) + panelColor = new PanelColor(col.get()); + else + panelColor = new PanelColor(Color.white); + panelColor.setBackground(comboType.getBackground()); + + panelPosition = new PanelPosition(Logo.messages.getString("3d.light.position"), light.getPosition()); + panelDirection = new PanelPosition(Logo.messages.getString("3d.light.direction"), light.getDirection()); + panelAngle = new PanelAngle(light.getAngle()); + ok = new JButton(Logo.messages.getString("pref.ok")); + refresh = new JButton(Logo.messages.getString("3d.light.apply")); + + Font font = WSManager.getWorkspaceConfig().getFont(); + + labelType.setFont(font); + comboType.setFont(font); + ok.setFont(font); + refresh.setFont(font); + + comboType.addActionListener(this); + comboType.setActionCommand("combo"); + ok.addActionListener(this); + ok.setActionCommand("ok"); + refresh.addActionListener(this); + refresh.setActionCommand("refresh"); + + getContentPane().add( + labelType, + new GridBagConstraints(0, 0, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, + new Insets(0, 0, 0, 0), 0, 0)); + getContentPane().add( + comboType, + new GridBagConstraints(1, 0, 1, 1, 1.0, 1.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, + new Insets(0, 0, 0, 0), 0, 0)); + getContentPane().add( + panelColor, + new GridBagConstraints(0, 1, 2, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, + new Insets(0, 0, 0, 0), 0, 0)); + getContentPane().add( + panelPosition, + new GridBagConstraints(0, 2, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, + new Insets(0, 0, 0, 0), 0, 0)); + getContentPane().add( + panelDirection, + new GridBagConstraints(1, 2, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, + new Insets(0, 0, 0, 0), 0, 0)); + getContentPane().add( + panelAngle, + new GridBagConstraints(0, 3, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, + new Insets(0, 0, 0, 0), 0, 0)); + getContentPane().add( + refresh, + new GridBagConstraints(0, 4, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, + new Insets(0, 0, 0, 0), 0, 0)); + getContentPane().add( + ok, + new GridBagConstraints(1, 4, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, + new Insets(0, 0, 0, 0), 0, 0)); + selectComponents(); + setVisible(true); + + } + + public void actionPerformed(ActionEvent e) + { + String cmd = e.getActionCommand(); + // The selected item in the combo Box has changed + if (cmd.equals("combo")) + selectComponents(); + // Button Ok pressed + else if (cmd.equals("ok")) + { + updateLight(); + dispose(); + } + // Button Apply pressed + else if (cmd.equals("refresh")) + { + updateLight(); + } + } + + private void updateLight() + { + int t = comboType.getSelectedIndex(); + Color3f c = new Color3f(panelColor.getValue()); + Point3f p = panelPosition.getPosition(); + Vector3f d = panelDirection.getDirection(); + float a = panelAngle.getAngleValue(); + light.setType(t); + light.setColor(c); + light.setPosition(p); + light.setDirection(d); + light.setAngle(a); + light.detach(); + light.removeAllChildren(); + light.createLight(); + // System.out.println(c+" "+" "+p+" "+d); + viewer3d.addNode(light); + + } + + private void selectComponents() + { + int id = comboType.getSelectedIndex(); + // None + if (id == MyLight.LIGHT_OFF) + { + panelColor.setEnabled(false); + panelPosition.setEnabled(false); + panelDirection.setEnabled(false); + panelAngle.setEnabled(false); + } + // Ambient + else if (id == MyLight.LIGHT_AMBIENT) + { + panelColor.setEnabled(true); + panelPosition.setEnabled(false); + panelDirection.setEnabled(false); + panelAngle.setEnabled(false); + } + // Directional + else if (id == MyLight.LIGHT_DIRECTIONAL) + { + panelColor.setEnabled(true); + panelPosition.setEnabled(false); + panelDirection.setEnabled(true); + panelAngle.setEnabled(false); + } + // PointLight + else if (id == MyLight.LIGHT_POINT) + { + panelColor.setEnabled(true); + panelPosition.setEnabled(true); + panelDirection.setEnabled(false); + panelAngle.setEnabled(false); + } + // Spot + else if (id == MyLight.LIGHT_SPOT) + { + panelColor.setEnabled(true); + panelPosition.setEnabled(true); + panelDirection.setEnabled(true); + panelAngle.setEnabled(true); + } + } + + class PanelAngle extends JPanel + { + private static final long serialVersionUID = 1L; + private JLabel label; + private JTextField angle; + private float angleValue; + + PanelAngle(float angleValue) + { + this.angleValue = angleValue; + + initGui(); + } + + private void initGui() + { + label = new JLabel(Logo.messages.getString("3d.light.angle")); + label.setFont(WSManager.getWorkspaceConfig().getFont()); + angle = new JTextField(String.valueOf(angleValue)); + angle.setSize(30, WSManager.getWorkspaceConfig().getFont().getSize() + 10); + add(label); + add(angle); + } + + public void setEnabled(boolean b) + { + super.setEnabled(b); + label.setEnabled(b); + angle.setEnabled(b); + } + + /** + * @return + * @uml.property name="angleValue" + */ + float getAngleValue() + { + try + { + float f = Float.parseFloat(angle.getText()); + return f; + } + catch (NumberFormatException e) + {} + return MyLight.DEFAULT_ANGLE; + } + } + + class PanelPosition extends JPanel + { + private static final long serialVersionUID = 1L; + private String title; + private JTextField Xpos; + private JTextField Ypos; + private JTextField Zpos; + private JLabel sep1; + private JLabel sep2; + Tuple3f tuple; + + PanelPosition(String title, Tuple3f tuple) + { + this.title = title; + this.tuple = tuple; + initGui(); + } + + private void initGui() + { + TitledBorder tb = BorderFactory.createTitledBorder(title); + tb.setTitleFont(WSManager.getWorkspaceConfig().getFont()); + setBorder(tb); + sep1 = new JLabel("x"); + sep2 = new JLabel("x"); + Xpos = new JTextField(4); + Ypos = new JTextField(4); + Zpos = new JTextField(4); + if (null != tuple) + { + Xpos.setText(String.valueOf((int) (tuple.x * 1000))); + Ypos.setText(String.valueOf((int) (tuple.y * 1000))); + Zpos.setText(String.valueOf((int) (tuple.z * 1000))); + } + add(Xpos); + add(sep1); + add(Ypos); + add(sep2); + add(Zpos); + } + + Point3f getPosition() + { + try + { + float x = Float.parseFloat(Xpos.getText()); + float y = Float.parseFloat(Ypos.getText()); + float z = Float.parseFloat(Zpos.getText()); + return new Point3f(x / 1000, y / 1000, z / 1000); + } + catch (NumberFormatException e) + {} + return (new Point3f(0, 0, 0)); + } + + Vector3f getDirection() + { + try + { + float x = Float.parseFloat(Xpos.getText()); + float y = Float.parseFloat(Ypos.getText()); + float z = Float.parseFloat(Zpos.getText()); + return new Vector3f(x / 1000, y / 1000, z / 1000); + } + catch (NumberFormatException e) + {} + return (new Vector3f(0, 0, 0)); + + } + + public void setEnabled(boolean b) + { + super.setEnabled(b); + sep1.setEnabled(b); + sep2.setEnabled(b); + Xpos.setEnabled(b); + Ypos.setEnabled(b); + Zpos.setEnabled(b); + } + + } + +} diff --git a/logo/src/xlogo/kernel/perspective/MyFog.java b/logo/src/xlogo/kernel/perspective/MyFog.java new file mode 100644 index 0000000..1961fcc --- /dev/null +++ b/logo/src/xlogo/kernel/perspective/MyFog.java @@ -0,0 +1,134 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +package xlogo.kernel.perspective; + +import javax.media.j3d.BranchGroup; +import javax.media.j3d.LinearFog; +import javax.media.j3d.ExponentialFog; +import javax.media.j3d.Fog; +import javax.media.j3d.BoundingSphere; + +import javax.vecmath.Color3f; + +public class MyFog extends BranchGroup +{ + protected final static int FOG_OFF = 0; + protected final static int FOG_LINEAR = 1; + protected final static int FOG_EXPONENTIAL = 2; + private int type = FOG_OFF; + private float density = 1; + private float backDistance = 3.5f; + private float frontDistance = 0.5f; + private Fog fog; + Color3f color; + + MyFog(int type, Color3f color) + { + super(); + setCapability(BranchGroup.ALLOW_DETACH); + setCapability(BranchGroup.ALLOW_CHILDREN_WRITE); + this.type = type; + this.color = color; + } + + /** + * This method creates a light according to each parameter:<br> + * type, color, position, direction and angle + */ + void createFog() + { + this.setCapability(BranchGroup.ALLOW_DETACH); + this.setCapability(BranchGroup.ALLOW_CHILDREN_WRITE); + switch (type) + { + case FOG_OFF: + fog = null; + break; + case FOG_LINEAR: + fog = new LinearFog(color); + ((LinearFog) fog).setBackDistance(backDistance); + ((LinearFog) fog).setFrontDistance(frontDistance); + fog.setInfluencingBounds(new BoundingSphere()); + break; + case FOG_EXPONENTIAL: + fog = new ExponentialFog(color, density); + fog.setInfluencingBounds(new BoundingSphere()); + break; + } + if (null != fog) + addChild(fog); + } + + /** + * This method returns the light type + * + * @return an integer which represents the light type + * @uml.property name="type" + */ + int getType() + { + return type; + } + + void setType(int t) + { + type = t; + } + + float getDensity() + { + return density; + } + + void setDensity(float f) + { + density = f; + } + + float getBack() + { + return backDistance; + } + + void setBack(float f) + { + backDistance = f; + } + + float getFront() + { + return frontDistance; + } + + void setFront(float f) + { + frontDistance = f; + } + +} diff --git a/logo/src/xlogo/kernel/perspective/MyLight.java b/logo/src/xlogo/kernel/perspective/MyLight.java new file mode 100644 index 0000000..9c5c054 --- /dev/null +++ b/logo/src/xlogo/kernel/perspective/MyLight.java @@ -0,0 +1,190 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +package xlogo.kernel.perspective; + +import javax.media.j3d.AmbientLight; +import javax.media.j3d.BoundingSphere; +import javax.media.j3d.DirectionalLight; +import javax.media.j3d.Light; +import javax.media.j3d.PointLight; +import javax.media.j3d.SpotLight; +import javax.vecmath.Color3f; +import javax.vecmath.Point3d; +import javax.vecmath.Point3f; +import javax.vecmath.Vector3f; +import javax.media.j3d.BranchGroup; + +public class MyLight extends BranchGroup +{ + protected final static int LIGHT_OFF = 0; + protected final static int LIGHT_AMBIENT = 1; + protected final static int LIGHT_DIRECTIONAL = 2; + protected final static int LIGHT_POINT = 3; + protected final static int LIGHT_SPOT = 4; + protected final static float DEFAULT_ANGLE = 15; + private int type = LIGHT_OFF; + private Color3f color; + private Point3f position; + private Vector3f direction; + private float angle = DEFAULT_ANGLE; + private Light light; + + MyLight(int type) + { + super(); + this.type = type; + } + + MyLight(int type, Color3f color, Point3f position) + { + super(); + this.type = type; + this.color = color; + this.position = position; + } + + /** + * This method creates a light according to each parameter:<br> + * type, color, position, direction and angle + */ + void createLight() + { + this.setCapability(BranchGroup.ALLOW_DETACH); + this.setCapability(BranchGroup.ALLOW_CHILDREN_WRITE); + switch (type) + { + case LIGHT_OFF: + light = null; + break; + case LIGHT_AMBIENT: + light = new AmbientLight(color); + light.setInfluencingBounds(new BoundingSphere(new Point3d(position), Double.MAX_VALUE)); + break; + case LIGHT_DIRECTIONAL: + light = new DirectionalLight(color, direction); + light.setInfluencingBounds(new BoundingSphere(new Point3d(position), Double.MAX_VALUE)); + break; + case LIGHT_POINT: + light = new PointLight(color, position, new Point3f(1, 0, 0)); + light.setInfluencingBounds(new BoundingSphere(new Point3d(position), Double.MAX_VALUE)); + break; + case LIGHT_SPOT: + light = new SpotLight(color, position, new Point3f(1, 0, 0), direction, (float) Math.toRadians(angle), + 64); + light.setInfluencingBounds(new BoundingSphere(new Point3d(position), Double.MAX_VALUE)); + break; + } + if (null != light) + addChild(light); + } + + /** + * This method returns the light type + * + * @return an integer which represents the light type + */ + int getType() + { + return type; + } + + /** + * @param t + */ + void setType(int t) + { + type = t; + } + + /** + * @return + */ + Color3f getColor() + { + return color; + } + + /** + * @param c + */ + void setColor(Color3f c) + { + color = c; + } + + /** + * @return + */ + Point3f getPosition() + { + return position; + } + + /** + * @param p + */ + void setPosition(Point3f p) + { + + position = p; + } + + /** + * @return + */ + Vector3f getDirection() + { + return direction; + } + + /** + * @param v + */ + void setDirection(Vector3f v) + { + direction = v; + } + + /** + * @return + */ + float getAngle() + { + return angle; + } + + /** + * @param f + */ + void setAngle(float f) + { + angle = f; + } + +} diff --git a/logo/src/xlogo/kernel/perspective/Viewer3D.java b/logo/src/xlogo/kernel/perspective/Viewer3D.java new file mode 100644 index 0000000..e7bce56 --- /dev/null +++ b/logo/src/xlogo/kernel/perspective/Viewer3D.java @@ -0,0 +1,493 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +package xlogo.kernel.perspective; + +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.Insets; +import java.awt.Toolkit; + +import javax.media.j3d.BoundingSphere; +import javax.media.j3d.BranchGroup; +import javax.media.j3d.Canvas3D; + +import java.awt.image.BufferedImage; + +import javax.media.j3d.ImageComponent2D; +import javax.media.j3d.ImageComponent; +import javax.media.j3d.GraphicsContext3D; +import javax.media.j3d.Shape3D; +import javax.media.j3d.Raster; +import javax.vecmath.Point3f; +import javax.media.j3d.Transform3D; +import javax.media.j3d.TransformGroup; +import javax.media.j3d.Node; +import javax.media.j3d.Background; + +import java.awt.GridBagLayout; +import java.awt.Color; + +import javax.swing.BorderFactory; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.border.TitledBorder; + +import java.awt.GridLayout; + +import javax.vecmath.Color3f; +import javax.vecmath.Point3d; +import javax.vecmath.Vector3d; + +import xlogo.storage.WSManager; +import xlogo.utils.Utils; +import xlogo.utils.WriteImage; +import xlogo.Logo; + +import com.sun.j3d.utils.behaviors.vp.OrbitBehavior; +import com.sun.j3d.utils.universe.SimpleUniverse; + +import java.awt.event.ActionListener; +import java.awt.event.ActionEvent; + +/** + * This Frame displays the 3D main Scene + * + * BG scene + * + * BG backBranchGroup | BG Mylight (4) | BG + * Background Light 1,2,3,4 Other objects + */ + +public class Viewer3D extends JFrame implements ActionListener +{ + private final static String ACTION_SCREENSHOT = "screenshot"; + private final static String ACTION_LIGHT0 = "light0"; + private final static String ACTION_LIGHT1 = "light1"; + private final static String ACTION_LIGHT2 = "light2"; + private final static String ACTION_LIGHT3 = "light3"; + private final static String ACTION_FOG = "fog"; + + // To store the Light attributes + private MyLight[] myLights; + // To store the Fog attributes + private MyFog myFog; + // Gui Components + private ImageIcon iscreenshot = Utils.dimensionne_image("screenshot.png", this); + private JButton screenshot; + private static final long serialVersionUID = 1L; + private World3D w3d; + private Canvas3D canvas3D; + /** + * Main scene's Branchgroup + */ + private BranchGroup scene; + private BranchManager branchManager; + private SimpleUniverse universe; + private Color3f backgroundColor; + private BranchGroup backBranchgroup; + private Background back; + private PanelLight panelLight; + private PanelFog panelFog; + + public Viewer3D(World3D w3d, Color c) + { + this.w3d = w3d; + this.backgroundColor = new Color3f(c); + initGui(); + } + + public void actionPerformed(ActionEvent e) + { + String cmd = e.getActionCommand(); + // Take a screenshot + if (cmd.equals(Viewer3D.ACTION_SCREENSHOT)) + { + WriteImage wi = new WriteImage(this, null); + int id = wi.chooseFile(); + if (id == JFileChooser.APPROVE_OPTION) + { + GraphicsContext3D ctx = canvas3D.getGraphicsContext3D(); + java.awt.Dimension scrDim = canvas3D.getSize(); + + // setting raster component + Raster ras = new Raster(new Point3f(-1.0f, -1.0f, -1.0f), Raster.RASTER_COLOR, 0, 0, scrDim.width, + scrDim.height, new ImageComponent2D(ImageComponent.FORMAT_RGB, new BufferedImage(scrDim.width, + scrDim.height, java.awt.image.BufferedImage.TYPE_INT_RGB)), null); + ctx.readRaster(ras); + BufferedImage img = ras.getImage().getImage(); + wi.setImage(img); + wi.start(); + } + } + // Click on Button Light1 + else if (cmd.equals(Viewer3D.ACTION_LIGHT0)) + { + new LightDialog(this, myLights[0], Logo.messages.getString("3d.light") + " 1"); + } + // Click on Button Light2 + else if (cmd.equals(Viewer3D.ACTION_LIGHT1)) + { + new LightDialog(this, myLights[1], Logo.messages.getString("3d.light") + " 2"); + } + // Click on Button Light3 + else if (cmd.equals(Viewer3D.ACTION_LIGHT2)) + { + new LightDialog(this, myLights[2], Logo.messages.getString("3d.light") + " 3"); + } + // Click on Button Light4 + else if (cmd.equals(Viewer3D.ACTION_LIGHT3)) + { + new LightDialog(this, myLights[3], Logo.messages.getString("3d.light") + " 4"); + } + // Click on the Fog Button + else if (cmd.equals(Viewer3D.ACTION_FOG)) + { + new FogDialog(this, myFog, Logo.messages.getString("3d.fog")); + + } + } + + public void setText() + { + setTitle(Logo.messages.getString("3d.viewer")); + panelFog.setText(); + panelLight.setText(); + + } + + private void initGui() + { + this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + setIconImage(Toolkit.getDefaultToolkit().createImage(Utils.class.getResource("icone.png"))); + + // Creation d'un composant de classe Canvas3D permettant de visualiser + // une scène 3D + canvas3D = new Canvas3D(SimpleUniverse.getPreferredConfiguration()); + // Création d'un univers 3D rattaché au composant 3D + universe = new SimpleUniverse(canvas3D); + + // Install the camera at the valid position with correct orientation + TransformGroup tg = universe.getViewingPlatform().getViewPlatformTransform(); + Transform3D trans = new Transform3D(); + if (null == w3d) + { + w3d = new World3D(); + } + trans.setTranslation(new Vector3d(-w3d.xCamera / 1000, -w3d.yCamera / 1000, -w3d.zCamera / 1000)); + Transform3D rot = new Transform3D(); + rot.lookAt(new Point3d(-w3d.xCamera / 1000, -w3d.yCamera / 1000, -w3d.zCamera / 1000), new Point3d(0, 0, 0), + new Vector3d(0, 1, 0)); + rot.invert(); + trans.mul(rot); + tg.setTransform(trans); + OrbitBehavior ob = new OrbitBehavior(canvas3D, OrbitBehavior.REVERSE_ALL); + ob.setRotationCenter(new Point3d(0, 0, 0)); + ob.setSchedulingBounds(new BoundingSphere(new Point3d(0, 0, 0), 2 * w3d.r / 1000)); + universe.getViewingPlatform().setViewPlatformBehavior(ob); + + // Create the root of the branch graph + scene = new BranchGroup(); + branchManager = new BranchManager(); + // scene.setName("Main Branch"); + // We can add New BranchGroup dynamically in the main scene + scene.setCapability(BranchGroup.ALLOW_CHILDREN_EXTEND); + // We can remove BranchGroup dynamically in the main scene + scene.setCapability(BranchGroup.ALLOW_CHILDREN_WRITE); + + // Configure and create background + createBackground(); + + // Configure Lights + initLights(); + + // Configure Fog + myFog = new MyFog(MyFog.FOG_OFF, backgroundColor); + scene.addChild(myFog); + + // Rattachement de la scène 3D à l'univers + universe.addBranchGraph(scene); + + Dimension d = Toolkit.getDefaultToolkit().getScreenSize(); + int width = d.width * 9 / 10; + int height = d.height * 9 / 10; + setSize(width, height); + getContentPane().setLayout(new GridBagLayout()); + screenshot = new JButton(iscreenshot); + screenshot.addActionListener(this); + screenshot.setMaximumSize(new Dimension(100, 100)); + screenshot.setActionCommand(Viewer3D.ACTION_SCREENSHOT); + + panelLight = new PanelLight(this); + panelFog = new PanelFog(this); + + getContentPane().add( + canvas3D, + new GridBagConstraints(0, 0, 1, 3, 2.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, + new Insets(0, 0, 0, 0), 0, 0)); + getContentPane().add( + screenshot, + new GridBagConstraints(1, 0, 1, 1, 0.1, 0.8, GridBagConstraints.CENTER, GridBagConstraints.NONE, + new Insets(10, 10, 10, 10), 0, 0)); + getContentPane().add( + panelLight, + new GridBagConstraints(1, 1, 1, 1, 0.1, 0.1, GridBagConstraints.CENTER, GridBagConstraints.NONE, + new Insets(10, 10, 10, 10), 0, 0)); + getContentPane().add( + panelFog, + new GridBagConstraints(1, 2, 1, 1, 0.1, 0.1, GridBagConstraints.CENTER, GridBagConstraints.NONE, + new Insets(10, 10, 10, 10), 0, 0)); + setText(); + getContentPane().validate(); + } + + /** + * This method adds a shape3D to the main scene + * + * @param s + * The Shape to add + */ + public void add3DObject(Shape3D s) + { + branchManager.add3DObject(s); + } + + public void add2DText(TransformGroup tg) + { + branchManager.add2DText(tg); + } + + public void insertBranch() + { + branchManager.insertBranch(); + branchManager = new BranchManager(); + } + + /** + * This methods adds two default PointLight int the 3d Scene + */ + private void initLights() + { + myLights = new MyLight[4]; + // First Default Point Light + Color3f color = new Color3f(1f, 1f, 1f); + Point3f pos = new Point3f((float) w3d.xCamera / 1000, (float) w3d.yCamera / 1000, (float) w3d.zCamera / 1000); + myLights[0] = new MyLight(MyLight.LIGHT_POINT, color, pos); + + // Second default Point Light + pos = new Point3f(-(float) w3d.xCamera / 1000, -(float) w3d.yCamera / 1000, -(float) w3d.zCamera / 1000); + myLights[1] = new MyLight(MyLight.LIGHT_POINT, color, pos); + + myLights[2] = new MyLight(MyLight.LIGHT_OFF); + myLights[3] = new MyLight(MyLight.LIGHT_OFF); + for (int i = 0; i < 4; i++) + { + myLights[i].createLight(); + } + addAllLights(scene); + } + + /** + * Add all lights in the main 3D scene + */ + private void addAllLights(BranchGroup bg) + { + for (int i = 0; i < 4; i++) + { + bg.addChild(myLights[i]); + } + } + + /** + * add a light in the main scene + */ + protected void addNode(Node l) + { + scene.addChild(l); + } + + /** + * This methods erase all drawings on the 3D Viewer Drawing Area + */ + public void clearScreen() + { + scene.removeAllChildren(); + createBackground(); + initLights(); + /* + * Enumeration<Locale> locales=universe.getAllLocales(); + * while(locales.hasMoreElements()){ + * Locale lo=locales.nextElement(); + * Enumeration<BranchGroup> en=lo.getAllBranchGraphs(); + * while(en.hasMoreElements()){ + * BranchGroup bg=en.nextElement(); + * // System.out.println(bg.getName()); + * if (null!=bg.getName()&&bg.getName().equals("Main Branch")){ + * // Detach scene + * bg.detach(); + * // Delete all nodes + * bg.removeAllChildren(); + * // create background + * createBackground(bg); + * // Add lights + * addAllLights(bg); + * // Attach scene + * lo.addBranchGraph(bg); + * } + * } + * } + */ + + } + + public void updateBackGround(Color c) + { + backgroundColor = new Color3f(c); + backBranchgroup.detach(); + createBackground(); + } + + /** + * This method creates the Background with the defined color + */ + public void createBackground() + { + backBranchgroup = new BranchGroup(); + backBranchgroup.setCapability(BranchGroup.ALLOW_DETACH); + backBranchgroup.setCapability(BranchGroup.ALLOW_CHILDREN_WRITE); + + back = new Background(backgroundColor); + back.setApplicationBounds(new BoundingSphere()); + back.setCapability(Background.ALLOW_COLOR_WRITE); + backBranchgroup.addChild(back); + scene.addChild(backBranchgroup); + } + + class BranchManager + { + BranchGroup bg; + + BranchManager() + { + bg = new BranchGroup(); + bg.setCapability(BranchGroup.ALLOW_DETACH); + } + + /** + * This method adds a shape3D to the main scene + * + * @param s + * The Shape to add + */ + void add3DObject(Shape3D s) + { + bg.addChild(s); + } + + void add2DText(TransformGroup tg) + { + bg.addChild(tg); + } + + void insertBranch() + { + bg.compile(); + scene.addChild(bg); + } + } + + class PanelLight extends JPanel + { + private static final long serialVersionUID = 1L; + private JButton[] buttonLights; + private Viewer3D viewer3d; + + PanelLight(Viewer3D viewer3d) + { + this.viewer3d = viewer3d; + initGui(); + } + + private void initGui() + { + buttonLights = new JButton[4]; + setLayout(new GridLayout(2, 2, 10, 10)); + for (int i = 0; i < 4; i++) + { + ImageIcon ilight = Utils.dimensionne_image("light" + i + ".png", viewer3d); + buttonLights[i] = new JButton(ilight); + add(buttonLights[i]); + buttonLights[i].addActionListener(viewer3d); + buttonLights[i].setActionCommand("light" + i); + } + setText(); + } + + void setText() + { + TitledBorder tb = BorderFactory.createTitledBorder(Logo.messages.getString("3d.light")); + tb.setTitleFont(WSManager.getWorkspaceConfig().getFont()); + setBorder(tb); + } + } + + class PanelFog extends JPanel + { + + private static final long serialVersionUID = 1L; + private JButton buttonFog; + private Viewer3D viewer3d; + + PanelFog(Viewer3D viewer3d) + { + this.viewer3d = viewer3d; + initGui(); + } + + private void initGui() + { + ImageIcon ifog =Utils.dimensionne_image("fog.png", viewer3d); + buttonFog = new JButton(ifog); + buttonFog.setActionCommand(Viewer3D.ACTION_FOG); + buttonFog.addActionListener(viewer3d); + add(buttonFog); + setText(); + } + + void setText() + { + TitledBorder tb = BorderFactory.createTitledBorder(Logo.messages.getString("3d.fog")); + tb.setTitleFont(WSManager.getWorkspaceConfig().getFont()); + setBorder(tb); + } + } + +} diff --git a/logo/src/xlogo/kernel/perspective/World3D.java b/logo/src/xlogo/kernel/perspective/World3D.java new file mode 100644 index 0000000..d3ffcc5 --- /dev/null +++ b/logo/src/xlogo/kernel/perspective/World3D.java @@ -0,0 +1,318 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written by Lo�c Le Coq, + * modifications, extensions, refactorings might have been applied by Marko Zivkovic + */ + +package xlogo.kernel.perspective; + +import xlogo.storage.WSManager; +import xlogo.storage.user.UserConfig; + +public class World3D +{ + + double theta = Math.toRadians(45); + + double phi = Math.toRadians(30); + + public double r = 1500; + + public double xCamera = r * Math.cos(theta) * Math.cos(phi); + + public double yCamera = r * Math.sin(theta) * Math.cos(phi); + + public double zCamera = r * Math.sin(phi); ; + + public double screenDistance = 1000; + + private double[][] array3D; + + public World3D() + { + initArray3D(); + + } + + /** + * This method converts the coordinates in coord to the screen coord + */ + public void toScreenCoord(double[] coord) + { + // Coord in the camera world + toCameraWorld(coord); + /* + * double x=array3D[0][0]*coord[0]+array3D[1][0]*coord[1]; + * double + * y=array3D[0][1]*coord[0]+array3D[1][1]*coord[1]+array3D[2][1]*coord + * [2]; + * double + * z=array3D[0][2]*coord[0]+array3D[1][2]*coord[1]+array3D[2][2]*coord + * [2]+r; + */ + cameraToScreen(coord); + } + + /** + * This method converts coordinates conatined in coord from camera world to + * screen + */ + public void cameraToScreen(double[] coord) + { + UserConfig uc = WSManager.getUserConfig(); + + double x = coord[0]; + double y = coord[1]; + double z = coord[2]; + coord[0] = screenDistance * x / z + uc.getImageWidth() / 2; + coord[1] = uc.getImageHeight() / 2 - screenDistance * y / z; + } + + /** + * This method initializes the 3D array + */ + private void initArray3D() + { + double cost = Math.cos(theta); + double sint = Math.sin(theta); + double cosp = Math.cos(phi); + double sinp = Math.sin(phi); + array3D = new double[4][4]; + array3D[0][0] = -sint; + array3D[0][1] = -cost * sinp; + array3D[0][2] = -cost * cosp; + array3D[0][3] = 0; + array3D[1][0] = cost; + array3D[1][1] = -sint * sinp; + array3D[1][2] = -sint * cosp; + array3D[1][3] = 0; + array3D[2][0] = 0; + array3D[2][1] = cosp; + array3D[2][2] = -sinp; + array3D[2][3] = 0; + array3D[3][0] = 0; + array3D[3][1] = 0; + array3D[3][2] = r; + array3D[3][3] = 1; + } + + /** + * This method converts coordinates from Real world to coodinates in camera + * world + * + * @param coord + * The real World coordinates + * @return Array that contains coordinates in camera world + */ + + public void toCameraWorld(double[] coord) + { + double x = array3D[0][0] * coord[0] + array3D[1][0] * coord[1]; + double y = array3D[0][1] * coord[0] + array3D[1][1] * coord[1] + array3D[2][1] * coord[2]; + double z = array3D[0][2] * coord[0] + array3D[1][2] * coord[1] + array3D[2][2] * coord[2] + r; + coord[0] = x; + coord[1] = y; + coord[2] = z; + } + + /** + * This method multiply two matrices + * + * @param a + * The first matrix + * @param b + * The second matrix + * @return The matrix product + */ + public double[][] multiply(double[][] a, double[][] b) + { + int n = a.length; + int p = b[0].length; + int s = a[0].length; + double[][] m = new double[n][p]; + for (int i = 0; i < n; i++) + { + for (int j = 0; j < p; j++) + { + m[i][j] = 0; + for (int k = 0; k < s; k++) + { + m[i][j] += a[i][k] * b[k][j]; + } + } + } + return m; + } + + /** + * This method returns a matrix for a Z Axis rotation + * + * @param angle + * The rotation angle + * @return The rotation matrix + */ + public double[][] rotationZ(double angle) + { + double[][] m = new double[3][3]; + double cos = Math.cos(Math.toRadians(angle)); + double sin = Math.sin(Math.toRadians(angle)); + m[0][0] = cos; + m[1][0] = sin; + m[0][1] = -sin; + m[1][1] = cos; + m[2][2] = 1; + m[2][1] = m[2][0] = m[0][2] = m[1][2] = 0; + return m; + } + + /** + * This method returns a matrix for a Y Axis rotation + * + * @param angle + * The rotation angle + * @return The rotation matrix + */ + public double[][] rotationY(double angle) + { + double[][] m = new double[3][3]; + double cos = Math.cos(Math.toRadians(angle)); + double sin = Math.sin(Math.toRadians(angle)); + m[0][0] = cos; + m[2][0] = sin; + m[0][2] = -sin; + m[2][2] = cos; + m[1][1] = 1; + m[0][1] = m[1][0] = m[1][2] = m[2][1] = 0; + return m; + } + + /** + * This method returns a matrix for a X Axis rotation + * + * @param angle + * The rotation angle + * @return The rotation matrix + */ + public double[][] rotationX(double angle) + { + double[][] m = new double[3][3]; + double cos = Math.cos(Math.toRadians(angle)); + double sin = Math.sin(Math.toRadians(angle)); + m[0][0] = 1; + m[1][1] = cos; + m[2][1] = sin; + m[1][2] = -sin; + m[2][2] = cos; + m[1][0] = m[2][0] = m[0][1] = m[0][2] = 0; + return m; + } + + /** + * This method with the 3 Euler's angle builds a rotation matrix + * + * @param heading + * The turtle heading + * @param roll + * The turtle roll + * @param pitch + * The turtle pitch + * @return The rotation Matrix + */ + + public double[][] EulerToRotation(double roll, double pitch, double heading) + { + double[][] m = new double[3][3]; + double rpitch = Math.toRadians(pitch); + double rheading = Math.toRadians(heading); + double rroll = Math.toRadians(roll); + double a = Math.cos(rpitch); + double b = Math.sin(rpitch); + double c = Math.cos(rroll); + double d = Math.sin(rroll); + double e = Math.cos(rheading); + double f = Math.sin(rheading); + double bd = b * d; + double bc = b * c; + m[0][0] = c * e - bd * f; + m[0][1] = -c * f - bd * e; + m[0][2] = -a * d; + m[1][0] = a * f; + m[1][1] = a * e; + m[1][2] = -b; + m[2][0] = d * e + bc * f; + m[2][1] = -d * f + bc * e; + m[2][2] = a * c; + return m; + } + + /** + * + */ + public double[] rotationToEuler(double[][] m) + { + double[] v = new double[3]; + + double angle_x, angle_y, angle_z; + double a, tr_x, tr_y; + // Angle x + angle_x = -Math.asin(m[1][2]); + + a = Math.cos(angle_x); + // Gimbal Lock? + if (Math.abs(a) > 0.005) + { + // No gimbal Lock + // Angle z + tr_x = m[1][1] / a; + tr_y = m[1][0] / a; + angle_z = Math.atan2(tr_y, tr_x); + // Angle y + tr_x = m[2][2] / a; + tr_y = -m[0][2] / a; + angle_y = Math.atan2(tr_y, tr_x); + v[0] = Math.toDegrees(angle_x); + v[1] = -Math.toDegrees(angle_y); + v[2] = -Math.toDegrees(angle_z); + } + else + { // Gimbal Lock + angle_y = 0; + // Angle z + tr_x = m[0][0]; + tr_y = m[2][0]; + angle_z = Math.atan2(tr_y, tr_x); + v[0] = 270; + v[1] = 0; + v[2] = Math.toDegrees(angle_z); + } + + for (int i = 0; i < v.length; i++) + { + if (v[i] < 0) + v[i] += 360; + } + return v; + } +} diff --git a/logo/src/xlogo/kernel/userspace/ErrorManager.java b/logo/src/xlogo/kernel/userspace/ErrorManager.java new file mode 100644 index 0000000..f1b18ec --- /dev/null +++ b/logo/src/xlogo/kernel/userspace/ErrorManager.java @@ -0,0 +1,201 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program 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 2 of the License, or (at your option)
+ * any later version. This program is 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 program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeMap;
+
+import xlogo.interfaces.ErrorDetector.AmbiguityDetector.AmbiguityListener;
+import xlogo.interfaces.ErrorDetector.FileErrorCollector;
+import xlogo.kernel.userspace.procedures.Procedure;
+
+public class ErrorManager implements FileErrorCollector
+{
+ private final FileErrorCollector fileErrorDetector;
+ private final AmbiguityDetector ambiguityDetector;
+
+ private final HashMap<String, Void> errorFiles = new HashMap<String, Void>();
+ private final HashMap<String, Collection<String>> ambiguousProcToFiles = new HashMap<String, Collection<String>>();
+
+ private final ArrayList<ErrorListener> errorListeners = new ArrayList<ErrorListener>();
+
+ public ErrorManager(FileErrorCollector fileErrorDetector, AmbiguityDetector ambiguityDetector)
+ {
+ this.fileErrorDetector = fileErrorDetector;
+ this.ambiguityDetector = ambiguityDetector;
+
+ initListeners();
+ }
+
+ private void initListeners()
+ {
+ fileErrorDetector.addErrorListener(new ErrorListener(){
+
+ @Override
+ public void errorsDetected(String fileName)
+ {
+ errorFiles.put(fileName, null);
+ notifyErrorDetected(fileName);
+ }
+
+ @Override
+ public void allErrorsCorrected(String fileName)
+ {
+ errorFiles.remove(fileName);
+
+ for (Collection<String> fileNames : ambiguousProcToFiles.values())
+ {
+ if (fileNames.contains(fileName))
+ return;
+ }
+ // no more errors or ambiguities found
+ notifyAllErrorsCorrected(fileName);
+ }
+ });
+
+ ambiguityDetector.addAmbiguityListener(new AmbiguityListener(){
+
+ @Override
+ public void ambiguityResolved(String procedureName, String fileName)
+ {
+ Collection<String> ambigFiles = ambiguousProcToFiles.get(procedureName);
+
+ ambigFiles.remove(fileName);
+
+ if (ambigFiles.size() == 0)
+ ambiguousProcToFiles.remove(procedureName);
+
+ // [this check is not necessary. if it was ambiguous, it did not have errors.]
+ //if (errorFiles.containsKey(fileName))
+ // return;
+ // No more file errors
+
+ for (Collection<String> fileNames : ambiguousProcToFiles.values())
+ {
+ if (fileNames.contains(fileName))
+ return;
+ }
+ // No more ambiguities for file
+
+ notifyAllErrorsCorrected(fileName);
+ }
+
+ @Override
+ public void ambiguityDetected(String procedureName, Map<String, Procedure> fileToProcedure)
+ {
+ ambiguousProcToFiles.put(procedureName, new ArrayList<String>(fileToProcedure.keySet()));
+ for (String fileName : fileToProcedure.keySet())
+ notifyErrorDetected(fileName);
+ }
+ });
+ }
+
+ @Override
+ public boolean hasErrors()
+ {
+ return errorFiles.size() > 0 || ambiguousProcToFiles.size() > 0;
+ }
+
+ @Override
+ public boolean hasErrors(String fileName)
+ {
+ if (errorFiles.containsKey(fileName))
+ return true;
+
+ for (Collection<String> conflictingFiles : ambiguousProcToFiles.values())
+ if (conflictingFiles.contains(fileName))
+ return true;
+ return false;
+ }
+
+ @Override
+ public Collection<ProcedureErrorMessage> getAllErrors()
+ {
+ ArrayList<ProcedureErrorMessage> allErrorMessages = new ArrayList<ProcedureErrorMessage>();
+ // Not the most efficient impl possible
+ allErrorMessages.addAll(fileErrorDetector.getAllErrors());
+ allErrorMessages.addAll(ambiguityDetector.getAllErrors());
+ return allErrorMessages;
+ }
+
+ @Override
+ public Collection<String> getAllErroneousFiles()
+ {
+ TreeMap<String, Void> allErrorFiles = new TreeMap<String, Void>();
+
+ for(String fileName : errorFiles.keySet())
+ allErrorFiles.put(fileName, null);
+
+ for(Collection<String> files : ambiguousProcToFiles.values())
+ for(String fileName : files)
+ allErrorFiles.put(fileName, null);
+
+ return allErrorFiles.keySet();
+ }
+
+ public Collection<String> getErrorMessages(String fileName)
+ {
+ ArrayList<String> messages = new ArrayList<String>();
+
+ for(ProcedureErrorMessage pem : getAllErrors())
+ if(pem.fileNames.contains(fileName))
+ messages.add(pem.toString());
+
+ return messages;
+ }
+
+ // These error event will directly update the gui => run on event dispatcher thread
+
+ @Override
+ public void addErrorListener(ErrorListener listener)
+ {
+ errorListeners.add(listener);
+ }
+
+ @Override
+ public void removeErrorListener(ErrorListener listener)
+ {
+ errorListeners.remove(listener);
+ }
+
+ private void notifyErrorDetected(final String fileName)
+ {
+ for (ErrorListener listener : errorListeners)
+ listener.errorsDetected(fileName);
+ }
+
+ private void notifyAllErrorsCorrected(final String fileName)
+ {
+ for (ErrorListener listener : errorListeners)
+ listener.allErrorsCorrected(fileName);
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/userspace/GlobalVariableTable.java b/logo/src/xlogo/kernel/userspace/GlobalVariableTable.java new file mode 100644 index 0000000..7d74560 --- /dev/null +++ b/logo/src/xlogo/kernel/userspace/GlobalVariableTable.java @@ -0,0 +1,73 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program 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 2 of the License, or (at your option)
+ * any later version. This program is 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 program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace;
+
+import java.util.HashMap;
+import java.util.Set;
+
+public class GlobalVariableTable
+{
+ /**
+ * All defined variables with their current value.
+ */
+ protected HashMap<String, String> globale;
+
+ public GlobalVariableTable()
+ {
+ globale = new HashMap<String, String>();
+ }
+
+ public Set<String> getVariables()
+ {
+ return globale.keySet();
+ }
+
+ public String getValue(String var)
+ {
+ return globale.get(var.toLowerCase());
+ }
+
+ public void define(String var, String value)
+ {
+ globale.put(var.toLowerCase(), value);
+ }
+
+ public void deleteVariable(String st)
+ {
+ globale.remove(st.toLowerCase());
+ }
+
+ /**
+ * Delete all Variables from the workspace
+ */
+ public void deleteAllVariables()
+ {
+ globale.clear();
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/userspace/ProcedureErrorMessage.java b/logo/src/xlogo/kernel/userspace/ProcedureErrorMessage.java new file mode 100644 index 0000000..2e2afc8 --- /dev/null +++ b/logo/src/xlogo/kernel/userspace/ProcedureErrorMessage.java @@ -0,0 +1,106 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program 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 2 of the License, or (at your option)
+ * any later version. This program is 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 program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import xlogo.Logo;
+import xlogo.kernel.userspace.procedures.ProcedureErrorType;
+
+/**
+ * This is used to report document structure errors
+ * or ambiguity errors, either within or among files.
+ * @author Marko Zivkovic
+ *
+ */
+public class ProcedureErrorMessage
+{
+ ProcedureErrorType type;
+ String procedureDescription;
+ Collection <String> fileNames;
+
+ /**
+ * The description may be either the name (if known) or the line, where the error was found.
+ * @param type
+ * @param procedureDescription
+ * @param fileName
+ */
+ public ProcedureErrorMessage(ProcedureErrorType type, String procedureDescription, String fileName)
+ {
+ this.type = type;
+ this.procedureDescription = procedureDescription;
+ this.fileNames = new ArrayList<String>();
+ this.fileNames.add(fileName);
+ }
+
+ /**
+ * This can be used for ambiguity messages
+ * @see #ProcedureErrorMessage(ProcedureErrorType, String, String)
+ */
+ public ProcedureErrorMessage(ProcedureErrorType type, String procedureDescription, Collection<String> fileNames)
+ {
+ this.type = type;
+ this.procedureDescription = procedureDescription;
+ this.fileNames = fileNames;
+ }
+
+ public ProcedureErrorType getErrorType()
+ {
+ return type;
+ }
+
+ public String getProcedureDescription()
+ {
+ return procedureDescription;
+ }
+
+ public Collection<String> getFileNames()
+ {
+ return fileNames;
+ }
+
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ for (String fileName : fileNames)
+ {
+ sb.append(fileName);
+ sb.append(", ");
+ }
+ sb.delete(sb.length()-2, sb.length()-1);
+
+ sb.append(procedureDescription);
+ sb.append(": ");
+ sb.append(Logo.messages.getString(type.getDescription()));
+
+ return sb.toString();
+ }
+
+}
\ No newline at end of file diff --git a/logo/src/xlogo/kernel/userspace/PropertyListTable.java b/logo/src/xlogo/kernel/userspace/PropertyListTable.java new file mode 100644 index 0000000..7764d60 --- /dev/null +++ b/logo/src/xlogo/kernel/userspace/PropertyListTable.java @@ -0,0 +1,165 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program 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 2 of the License, or (at your option)
+ * any later version. This program is 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 program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace;
+
+import java.util.HashMap;
+import java.util.Set;
+
+public class PropertyListTable
+{
+
+ /**
+ * For all Property Lists
+ */
+ private HashMap<String, HashMap<String, String>> propList;
+
+ public PropertyListTable()
+ {
+ propList = new HashMap<String, HashMap<String,String>>();
+ }
+
+
+ /**
+ * This method adds in the property List called "name" a value for the
+ * corresponding key
+ *
+ * @param name
+ * The property List 's name
+ * @param key
+ * The key for the value to add
+ * @param value
+ * The value to add
+ */
+ public void addPropList(String name, String key, String value)
+ {
+ if (!propList.containsKey(name))
+ {
+ propList.put(name, new HashMap<String, String>());
+ }
+ propList.get(name).put(key, value);
+ }
+
+ /**
+ * This method removes a Property List
+ *
+ * @param name
+ * The property List 's name
+ */
+ public void removePropList(String name)
+ {
+ if (propList.containsKey(name))
+ {
+ propList.remove(name);
+ }
+ }
+
+ /**
+ * This method removes a couple (key, value) from a Property List
+ *
+ * @param name
+ * The property List 's name
+ * @param key
+ * The key to delete
+ */
+ public void removePropList(String name, String key)
+ {
+ if (propList.containsKey(name))
+ {
+ if (propList.get(name).containsKey(key))
+ propList.get(name).remove(key);
+ if (propList.get(name).isEmpty())
+ propList.remove(name);
+ }
+ }
+
+ /**
+ * This method returns a list that contains all couple key value
+ *
+ * @param name
+ * The Property List's name
+ * @return A list with all keys-values
+ */
+ public String displayPropList(String name)
+ {
+ if (propList.containsKey(name))
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append("[ ");
+ Set<String> set = propList.get(name).keySet();
+ for (String key : set)
+ {
+ sb.append(key);
+ sb.append(" ");
+ String value = propList.get(name).get(key);
+ if (value.startsWith("\""))
+ value = value.substring(1);
+ sb.append(value);
+ sb.append(" ");
+ }
+ sb.append("] ");
+ return sb.toString();
+ }
+ else
+ return "[ ] ";
+ }
+
+ /**
+ * This method return a value from a Property List
+ *
+ * @param name
+ * The Property List's name
+ * @param key
+ * The key for the chosen value
+ * @return The value for this key
+ */
+ public String getPropList(String name, String key)
+ {
+ if (!propList.containsKey(name)) { return "[ ]"; }
+ if (!propList.get(name).containsKey(key))
+ return "[ ]";
+ return propList.get(name).get(key);
+ }
+
+ /**
+ * Returns all defined Property List names
+ *
+ * @return A list with all Property List names
+ */
+ public Set<String> getPropListKeys()
+ {
+ return propList.keySet();
+ }
+
+ /**
+ * Delete all property Lists from the workspace
+ */
+ public void deleteAllPropertyLists()
+ {
+ propList.clear();
+ }
+}
diff --git a/logo/src/xlogo/kernel/userspace/UserSpace.java b/logo/src/xlogo/kernel/userspace/UserSpace.java new file mode 100644 index 0000000..d3b79d6 --- /dev/null +++ b/logo/src/xlogo/kernel/userspace/UserSpace.java @@ -0,0 +1,790 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were entirely written by Marko Zivkovic + */ + +package xlogo.kernel.userspace; + +import java.util.ArrayList; +import java.util.Collection; + +import xlogo.Logo; +import xlogo.interfaces.BroadcasterErrorFileContainer; +import xlogo.interfaces.X4SModeSwitcher; +import xlogo.interfaces.ErrorDetector.FileErrorCollector; +import xlogo.kernel.gui.GuiMap; +import xlogo.kernel.userspace.context.ContextManager; +import xlogo.kernel.userspace.context.LogoContext; +import xlogo.kernel.userspace.files.LogoFileContainer; +import xlogo.kernel.userspace.files.LogoFilesManager; +import xlogo.kernel.userspace.procedures.ExecutablesProvider; +import xlogo.kernel.userspace.procedures.Procedure; +import xlogo.kernel.userspace.procedures.ProceduresManager; +import xlogo.messages.MessageKeys; +import xlogo.messages.async.dialog.DialogMessenger; + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; + +/** + * This is a facade for what was before called Workspace in XLogo, and much more. + * XLogo's Workspace had: + * <p> + * - All Defined Procedures <br> + * - All Global variables <br> + * - All GUI Objects (generated by a Logo program)<br> + * - All Property lists + * <p> + * The new "Workspace" does not resemble the old workspace at all, although it still provides access to these symbol tables. + * <p> + * New in XLogo4Schools: multiple Files. Procedures must be unique within all files of a context. + * If multiple procedures with the same name are defined <br> + * - within one file : The file will not be usable, until errors and ambiguities are fixed (like in XLogo), <br> + * - among multiple files : All of these files will not be executable, <br> //TODO or the ambiguous procedures will not be executed + * unless the ambiguity is resolved. + * <p> + * New are also the Contexts for user mode, contest mode, network mode. + * The contest mode is completely new. In XLogo, Network mode was implemented by replacing the whole workspace. + * Now only the Contexts with symbol tables are replaced. Why? Because we have an event driven architecture now. + * If the whole workspace (referring to XLogo's workspace, including event dispatchers) would be replaced, then all subscribers to the workspace would have to be re-mapped. + * By keeping the LogoFileManager and the ProceduresManager, subscribers to the user space (which are redirected to the managers) must not be re-mapped when a context switch happens. + * <p> + * (Option for future: Procedures can be qualified with + * {@code fileName.procedureName}. But : A dot is not a legal character for a + * name anymore) + * + * @author Marko Zivkovic + * + */ +public class UserSpace implements X4SModeSwitcher, LogoFileContainer, BroadcasterErrorFileContainer, + ExecutablesProvider, FileErrorCollector +{ + + /** + * This is included for every call that is delegated. This way all errors are caught and if we're lucky, + * The application can still be used or terminated normally. At least the user has discovered a new bug and he or she + * can report it. + * + * TODO write a log file? + * + * @param e + */ + public static void showErrorDialog(Exception e) + { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + e.printStackTrace(pw); + + DialogMessenger.getInstance().dispatchError(Logo.messages.getString(MessageKeys.WS_ERROR_TITLE), + sw.toString()); + } + + private final ContextManager contextManager; // Switches context when modes change, notifies the other managers. & causes mode and clock events + private final LogoFilesManager filesManager; // handles file specific requests & causes file events + private final ProceduresManager proceduresManager; // handles procedure specific requests & causes defined/undefined events + private final ErrorManager errorManager; // handles error specific requests & causes error events + + public UserSpace() + { + contextManager = new ContextManager(); + proceduresManager = new ProceduresManager(contextManager); + filesManager = new LogoFilesManager(contextManager); + errorManager = new ErrorManager(filesManager, proceduresManager); + } + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * X4S MODE SWITCHER + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + */ + + @Override + public String getSerializedContext() + { + try + { + return contextManager.getSerializedContext(); + } + catch (Exception e) { showErrorDialog(e); } + return ""; + } + + @Override + public boolean isNetworkMode() + { + try + { + return contextManager.isNetworkMode(); + } + catch (Exception e) { showErrorDialog(e); } + return false; + } + + @Override + public boolean isRecordMode() + { + try + { + return contextManager.isRecordMode(); + } + catch (Exception e) { showErrorDialog(e); } + return false; + } + + @Override + public boolean isUserMode() + { + try + { + return contextManager.isUserMode(); + } + catch (Exception e) { showErrorDialog(e); } + return true; + } + + @Override + public void startRecordMode(String[] fileNames) throws IllegalArgumentException, IOException + { + try + { + contextManager.startRecordMode(fileNames); + } + catch (Exception e) { showErrorDialog(e); } + } + + @Override + public void stopRecordMode() + { + try + { + contextManager.stopRecordMode(); + } + catch (Exception e) { showErrorDialog(e); } + } + + public void pushNetworkMode(String networkContext) + { + try + { + contextManager.pushNetworkMode(networkContext); + } + catch (Exception e) { showErrorDialog(e); } + } + + @Override + public void popNetworkMode() + { + try + { + contextManager.popNetworkMode(); + } + catch (Exception e) { showErrorDialog(e); } + } + + @Override + public void addModeChangedListener(ModeChangeListener listener) + { + try + { + contextManager.addModeChangedListener(listener); + } + catch (Exception e) { showErrorDialog(e); } + } + + @Override + public void removeModeChangedListener(ModeChangeListener listener) + { + try + { + contextManager.removeModeChangedListener(listener); + } + catch (Exception e) { showErrorDialog(e); } + } + + @Override + public void addBroadcastListener(MessageListener listener) + { + try + { + contextManager.addBroadcastListener(listener); + } + catch (Exception e) { showErrorDialog(e); } + } + + @Override + public void removeBroadcastListener(MessageListener listener) + { + try + { + contextManager.removeBroadcastListener(listener); + } + catch (Exception e) { showErrorDialog(e); } + } + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * LOGO FILE CONTAINER + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + */ + + @Override + public String[] getFileNames() + { + try + { + return filesManager.getFileNames(); + } + catch (Exception e) { showErrorDialog(e); } + return new String[0]; + } + + @Override + public String readFile(String name) + { + try + { + return filesManager.readFile(name); + } + catch (Exception e) { showErrorDialog(e); } + return ""; + } + + @Override + public String getOpenFileName() + { + try + { + return filesManager.getOpenFileName(); + } + catch (Exception e) { showErrorDialog(e); } + return null; + } + + @Override + public boolean existsFile(String name) + { + try + { + return filesManager.existsFile(name); + } + catch (Exception e) { showErrorDialog(e); } + return false; + } + + @Override + public void createFile(String fileName) throws IOException + { + try + { + filesManager.createFile(fileName); + } + catch (Exception e) { showErrorDialog(e); } + } + + @Override + public void removeFile(String fileName) + { + try + { + filesManager.removeFile(fileName); + } + catch (Exception e) { showErrorDialog(e); } + } + + /** + * The file is expected to exist on the file system and to have a .lgo extension + * @param filePath + * @throws IOException + */ + public void importFile(File filePath) throws IOException + { + try + { + filesManager.importFile(filePath); + } + catch (Exception e) { showErrorDialog(e); } + } + + /** + * @throws IOException + * @see {@link LogoFilesManager#exportFile(String, File)} + */ + public void exportFile(String fileName, File dest) throws IOException + { + try + { + filesManager.exportFile(fileName, dest); + } + catch (Exception e) { showErrorDialog(e); } + } + + /** + * @throws IOException + * @see {@link LogoFilesManager#exportFile(String, File, String)} + */ + public void exportFile(String fileName, File location, String targetName) throws IOException + { + try + { + filesManager.exportFile(fileName, location, targetName); + } + catch (Exception e) { showErrorDialog(e); } + } + + @Override + public String makeUniqueFileName(String base) + { + try + { + return filesManager.makeUniqueFileName(base); + } + catch (Exception e) { showErrorDialog(e); } + return null; + } + + @Override + public void renameFile(String oldName, String newName) + { + try + { + filesManager.renameFile(oldName, newName); + } + catch (Exception e) { showErrorDialog(e); } + } + + @Override + public void openFile(String fileName) + { + try + { + filesManager.openFile(fileName); + } + catch (Exception e) { showErrorDialog(e); } + } + + @Override + public void closeFile(String fileName) + { + try + { + filesManager.closeFile(fileName); + } + catch (Exception e) { showErrorDialog(e); } + } + + @Override + public void writeFileText(String fileName, String content) throws IOException + { + try + { + filesManager.writeFileText(fileName, content); + } + catch (Exception e) { showErrorDialog(e); } + } + + @Override + public void storeFile(String fileName) throws IOException + { + try + { + filesManager.storeFile(fileName); + } + catch (Exception e) { showErrorDialog(e); } + } + + @Override + public void addFileListener(FileContainerChangeListener listener) + { + try + { + filesManager.addFileListener(listener); + } + catch (Exception e) { showErrorDialog(e); } + } + + @Override + public void removeFileListener(FileContainerChangeListener listener) + { + try + { + filesManager.removeFileListener(listener); + } + catch (Exception e) { showErrorDialog(e); } + } + + public String getLastEditedFileName() + { + try + { + return filesManager.getLastEditedFileName(); + } + catch (Exception e) { showErrorDialog(e); } + return null; + } + + public boolean isFilesListEditable() + { + try + { + return filesManager.isFilesListEditable(); + } + catch (Exception e) { showErrorDialog(e); } + return true; + } + + public boolean hasErrors(String fileName) + { + try + { + return filesManager.hasErrors(fileName) || proceduresManager.hasAmbiguousProcedures(fileName); + } + catch (Exception e) { showErrorDialog(e); } + return false; + } + + @Override + public void editAll() + { + try + { + filesManager.editAll(); + } + catch (Exception e) { showErrorDialog(e); } + } + + /** + * Resets the UserSpace by deleting all files (and procedures), all + * variables and all property lists. + * <p> + * Note : deleting all files will cause a chain of events. + */ + public void eraseAll() + { + try + { + filesManager.eraseAll(); // should remove all files and procedures and fire the right events + LogoContext context = contextManager.getContext(); + context.getGlobals().deleteAllVariables(); + context.getPropertyLists().deleteAllPropertyLists(); + context.getGuiMap().clear(); + } + catch (Exception e) { showErrorDialog(e); } + } + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * PROCEDURE CONTAINER + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + */ + + /** + * This is the implementation of the Logo Command define. + * Its effect has changed since we have multiple files now, but the semantics are preserved. + * Logo programs will behave exactly as before. <p> + * If the procedure is ambiguous, cannot decide which one to redefine => IllegalArgumentException <br> + * If the procedure is already defined once, that definition will be redefined in its original LogoFile. <br> + * If the procedure is not yet defined, a new File will be created "Generated Procedures" if does not yet exist, + * and the new procedure will be put there. + * + * @throws IllegalArgumentException if the procedure's name is ambiguous in the current context: I Don't know which one to redefine. + * @throws IllegalArgumentException if the procedure is not executable or it's owner does not exist in this context. + * @throws IOException - if the file "gerated procedure" could not be created in the context. + */ + @Override + public void defineProcedure(Procedure procedure) throws IOException + { + try + { + String procedureName = procedure.getName(); + if (proceduresManager.isProcedureAmbiguous(procedureName)) + throw new IllegalArgumentException("Attempt to redefine ambiguous procedure."); + + if (!isExecutable(procedureName)) + { + String fileName = Logo.messages.getString("ws.generated.procedure"); + if (!existsFile(fileName)) + createFile(fileName); + } + + proceduresManager.defineProcedure(procedure); + } + catch (Exception e) { showErrorDialog(e); } + } + + @Override + public Collection<Procedure> getExecutables() + { + try + { + return proceduresManager.getExecutables(); + } + catch (Exception e) { showErrorDialog(e); } + return new ArrayList<Procedure>(); + } + + @Override + public Procedure getExecutable(String procedureName) + { + try + { + return proceduresManager.getExecutable(procedureName); + } + catch (Exception e) { showErrorDialog(e); } + return null; + } + + @Override + public boolean isExecutable(String procedureName) + { + try + { + return proceduresManager.isExecutable(procedureName); + } + catch (Exception e) { showErrorDialog(e); } + return false; + } + + @Override + public void eraseProcedure(String procedureName) + { + try + { + proceduresManager.eraseProcedure(procedureName); + } + catch (Exception e) { showErrorDialog(e); } + } + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * The following symbol tables do not need a manager and no events + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + */ + + public GuiMap getGuiMap() + { + try + { + return contextManager.getContext().getGuiMap(); + } + catch (Exception e) { showErrorDialog(e); } + return new GuiMap(); + } + + public PropertyListTable getPropertyLists() + { + try + { + return contextManager.getContext().getPropertyLists(); + } + catch (Exception e) { showErrorDialog(e); } + return new PropertyListTable(); + } + + public GlobalVariableTable getGlobals() + { + try + { + return contextManager.getContext().getGlobals(); + } + catch (Exception e) { showErrorDialog(e); } + return new GlobalVariableTable(); + } + + @Override + public Collection<String> getAllProcedureNames() + { + try + { + return proceduresManager.getAllProcedureNames(); + } + catch (Exception e) { showErrorDialog(e); } + return new ArrayList<String>(); + } + + @Override + public Collection<String> getAllProcedureNames(String fileName) + { + try + { + return proceduresManager.getAllProcedureNames(fileName); + } + catch (Exception e) { showErrorDialog(e); } + return new ArrayList<String>(); + } + + @Override + public String getProcedureOwner(String procedureName) + { + try + { + return proceduresManager.getProcedureOwner(procedureName); + } + catch (Exception e) { showErrorDialog(e); } + return null; + } + + @Override + public void addProcedureMapListener(ProcedureMapListener listener) + { + try + { + proceduresManager.addProcedureMapListener(listener); + } + catch (Exception e) { showErrorDialog(e); } + } + + @Override + public void removeProcedureMapListener(ProcedureMapListener listener) + { + try + { + proceduresManager.removeProcedureMapListener(listener); + } + catch (Exception e) { showErrorDialog(e); } + } + + /* + * Errors & Ambiguities + */ + + /** + * conflicting w.r.t. ambiguity of their procedures + */ + @Override + public Collection<String> getAllConflictingFiles() + { + try + { + return proceduresManager.getAllConflictingFiles(); + } + catch (Exception e) { showErrorDialog(e); } + return new ArrayList<String>(); + } + + @Override + public boolean hasAmbiguousProcedures(String fileName) + { + try + { + return proceduresManager.hasAmbiguousProcedures(fileName); + } + catch (Exception e) { showErrorDialog(e); } + return false; + } + + @Override + public boolean isProcedureAmbiguous(String procedureName) + { + try + { + return proceduresManager.isProcedureAmbiguous(procedureName); + } + catch (Exception e) { showErrorDialog(e); } + return false; + } + + @Override + public void addAmbiguityListener(AmbiguityListener listener) + { + try + { + proceduresManager.addAmbiguityListener(listener); + } + catch (Exception e) { showErrorDialog(e); } + } + + @Override + public void removeAmbiguityListener(AmbiguityListener listener) + { + try + { + proceduresManager.removeAmbiguityListener(listener); + } + catch (Exception e) { showErrorDialog(e); } + } + + @Override + public boolean hasErrors() + { + try + { + return errorManager.hasErrors(); + } + catch (Exception e) { showErrorDialog(e); } + return false; + } + + @Override + public Collection<ProcedureErrorMessage> getAllErrors() + { + try + { + return errorManager.getAllErrors(); + } + catch (Exception e) { showErrorDialog(e); } + return new ArrayList<ProcedureErrorMessage>(); + } + + @Override + public Collection<String> getAllErroneousFiles() + { + try + { + return errorManager.getAllErroneousFiles(); + } + catch (Exception e) { showErrorDialog(e); } + return new ArrayList<String>(); + + } + + @Override + public void addErrorListener(ErrorListener listener) + { + try + { + errorManager.addErrorListener(listener); + } + catch (Exception e) { showErrorDialog(e); } + } + + @Override + public void removeErrorListener(ErrorListener listener) + { + try + { + errorManager.removeErrorListener(listener); + } + catch (Exception e) { showErrorDialog(e); } + } + + public Collection<String> getErrorMessages(String fileName) + { + try + { + return errorManager.getErrorMessages(fileName); + } + catch (Exception e) { showErrorDialog(e); } + return new ArrayList<String>(); + } + +} diff --git a/logo/src/xlogo/kernel/userspace/context/ContextManager.java b/logo/src/xlogo/kernel/userspace/context/ContextManager.java new file mode 100644 index 0000000..fe5dab7 --- /dev/null +++ b/logo/src/xlogo/kernel/userspace/context/ContextManager.java @@ -0,0 +1,301 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program 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 2 of the License, or (at your option)
+ * any later version. This program is 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 program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace.context;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Stack;
+
+import xlogo.interfaces.MessageBroadcaster;
+import xlogo.interfaces.X4SModeSwitcher;
+
+/**
+ * One of the four main roles in the {@link xlogo.kernel.userspace.UserSpace} <br>
+ * <b> The Contexts and Priorities and multiplicity</b>
+ * 1. UserContext [1] : default context, lowest priority, cannot be killed.
+ * 2. RecordContext [0..1] : medium priority, killing it will kill all networking contexts too.
+ * 3. NetworkContext [0..*] : highest priority, can be stacked.
+ *
+ * @author Marko
+ */
+public class ContextManager implements X4SModeSwitcher, ContextSwitcher, MessageBroadcaster
+{
+ private LogoContext currentContext;
+
+ private final UserContext userContext = new UserContext();
+ private RecordContext recordContext;
+ private final Stack<NetworkContext> networkStack = new Stack<NetworkContext>();
+
+ private MessageListener contextMessageListener;
+
+ public ContextManager()
+ {
+ currentContext = userContext;
+ initContextMessageListener();
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * CONTEXT PROVIDER
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ /**
+ * After a context has been removed by stopXXXMode(), contextSwitch() will determine the next current context.
+ * It only peeks at the context collections, but does not modify them. Calling this does never harm,
+ * but after one of the context collections has been changed, it should be called.<br>
+ * It notifies ContextProviderListeners if the context has actually changed after calling this.
+ * <p>
+ * The policy for context switches is defines in the interface here : {@link X4SModeSwitcher}
+ * <p>
+ * Note, this should be called before the mode switch events, because we first want to completely context switch, before we change the mode.
+ * Generally always publish events after the event has actually occurred.
+ *
+ */
+ protected void contextSwitch()
+ {
+ LogoContext old = currentContext;
+
+ if (networkStack.size() > 0)
+ currentContext = networkStack.peek();
+ else
+ currentContext = recordContext != null ? recordContext : userContext;
+
+ if(old != currentContext)
+ notifyContextSwitched(currentContext);
+ }
+
+ /**
+ * returns the current context after the policy described in the in the interface {@link X4SModeSwitcher}
+ */
+ @Override
+ public LogoContext getContext()
+ {
+ return currentContext;
+ }
+
+ // Context switch : internal communication : no direct gui update, must not run on event dispatcher thread
+
+ private final ArrayList<ContextSwitchListener> contextSwitchListeners = new ArrayList<ContextSwitcher.ContextSwitchListener>();
+
+ @Override
+ public void addContextSwitchListener(ContextSwitchListener listener)
+ {
+ contextSwitchListeners.add(listener);
+ }
+
+ @Override
+ public void removeContextSwitchListener(ContextSwitchListener listener)
+ {
+ contextSwitchListeners.remove(listener);
+ }
+
+ public void notifyContextSwitched(LogoContext newContext)
+ {
+ for (ContextSwitchListener listener : contextSwitchListeners)
+ listener.contextSwitched(newContext);
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * MODE SWITCHER
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ @Override
+ public String getSerializedContext()
+ {
+ return currentContext.toString();
+ }
+
+ @Override
+ public boolean isUserMode()
+ {
+ return currentContext instanceof UserContext;
+ }
+
+ @Override
+ public boolean isRecordMode()
+ {
+ return currentContext instanceof RecordContext;
+ }
+
+ @Override
+ public boolean isNetworkMode()
+ {
+ return currentContext instanceof NetworkContext;
+ }
+
+ /**
+ * A record mode can only be started from user mode. In other words,
+ * a record mode cannot be started, if there is another <b>record or network</b> mode active.
+ * @throws IllegalStateException If current mode is not user mode.
+ * @throws IllegalArgumentException If the fileNames are not well formed, null, or ambiguous
+ * @throws IOException If the record files could not be created => recording is impossible => record mode is impossible
+ */
+ @Override
+ public void startRecordMode(String[] fileNames) throws IllegalArgumentException, IOException
+ {
+ if (!isUserMode())
+ throw new IllegalStateException();
+
+ recordContext = new RecordContext(fileNames);
+ recordContext.addBroadcastListener(contextMessageListener);
+
+ contextSwitch();
+
+ notifyContestModeStarted();
+ }
+
+ /**
+ * This will first regularly kill all network contexts, one after the other. Listeners will be notified for every kill. <br>
+ * Afterwards it will kill the current record context. <br>
+ * When this call returns, current mode is user mode.
+ *
+ * @throws IllegalStateException If there is no recordContext available
+ */
+ @Override
+ public void stopRecordMode() throws IllegalStateException
+ {
+ if (recordContext == null)
+ return; // might be a quick double klick that causes this
+
+ while(isNetworkMode())
+ popNetworkMode();
+
+ recordContext = null;
+
+ contextSwitch();
+
+ notifyRecordModeStopped();
+ }
+
+ /**
+ * @throws IllegalArgumentException If serializedContext is corrupted
+ */
+ @Override
+ public void pushNetworkMode(String serializedContext) throws IllegalArgumentException
+ {
+ NetworkContext nc = new NetworkContext(serializedContext);
+ networkStack.push(nc);
+
+ contextSwitch();
+
+ if (networkStack.size() == 1)
+ notifyNetworkModeStarted();
+ }
+
+ /**
+ * This will kill the current network context.
+ * @throws IllegalStateException - if there is no network context to kill.
+ */
+ @Override
+ public void popNetworkMode() throws IllegalStateException
+ {
+ if (!isNetworkMode())
+ throw new IllegalStateException("There is no network context to kill.");
+
+ networkStack.pop();
+
+ contextSwitch();
+
+ if (!isNetworkMode())
+ notifyNetworkModeStopped();
+ }
+
+ // Mode Change Listeners : update GUI => run on event dispatcher thread
+
+ private ArrayList<ModeChangeListener> modeChangeListeners = new ArrayList<ModeChangeListener>();
+
+ @Override
+ public void addModeChangedListener(ModeChangeListener listener)
+ {
+ modeChangeListeners.add(listener);
+ }
+
+ @Override
+ public void removeModeChangedListener(ModeChangeListener listener)
+ {
+ modeChangeListeners.remove(listener);
+ }
+
+ private void notifyContestModeStarted()
+ {
+ for (ModeChangeListener listener : modeChangeListeners)
+ listener.recordModeStarted();
+ }
+
+ private void notifyRecordModeStopped()
+ {
+ for (ModeChangeListener listener : modeChangeListeners)
+ listener.recordModeStopped();
+ }
+
+ private void notifyNetworkModeStarted()
+ {
+ for (ModeChangeListener listener : modeChangeListeners)
+ listener.networkModeStarted();
+ }
+
+ private void notifyNetworkModeStopped()
+ {
+ for (ModeChangeListener listener : modeChangeListeners)
+ listener.networkModeStopped();
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * CONTEXT MESSAGE LISTENERS (used to broadcast clock events from record mode)
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ private void initContextMessageListener()
+ {
+ contextMessageListener = new MessageListener(){
+
+ @Override
+ public void messageEvent(String source, String message)
+ {
+ for (MessageListener listener : broadcastListeners)
+ listener.messageEvent(source, message);
+ }
+ };
+ }
+
+ private final ArrayList<MessageListener> broadcastListeners = new ArrayList<MessageListener>();
+
+ @Override
+ public void addBroadcastListener(MessageListener listener)
+ {
+ broadcastListeners.add(listener);
+ }
+
+ @Override
+ public void removeBroadcastListener(MessageListener listener)
+ {
+ broadcastListeners.remove(listener);
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/userspace/context/ContextSwitcher.java b/logo/src/xlogo/kernel/userspace/context/ContextSwitcher.java new file mode 100644 index 0000000..e38c645 --- /dev/null +++ b/logo/src/xlogo/kernel/userspace/context/ContextSwitcher.java @@ -0,0 +1,43 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program 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 2 of the License, or (at your option)
+ * any later version. This program is 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 program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace.context;
+
+
+public interface ContextSwitcher
+{
+ public LogoContext getContext();
+
+ public void addContextSwitchListener(ContextSwitchListener listener);
+
+ public void removeContextSwitchListener(ContextSwitchListener listener);
+
+ public interface ContextSwitchListener
+ {
+ public void contextSwitched(LogoContext newContext);
+ }
+}
diff --git a/logo/src/xlogo/kernel/userspace/context/LogoContext.java b/logo/src/xlogo/kernel/userspace/context/LogoContext.java new file mode 100644 index 0000000..15bdc1f --- /dev/null +++ b/logo/src/xlogo/kernel/userspace/context/LogoContext.java @@ -0,0 +1,312 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program 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 2 of the License, or (at your option)
+ * any later version. This program is 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 program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace.context;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+import xlogo.Logo;
+import xlogo.interfaces.ProcedureMapper.ProcedureMapListener;
+import xlogo.kernel.gui.GuiMap;
+import xlogo.kernel.userspace.GlobalVariableTable;
+import xlogo.kernel.userspace.PropertyListTable;
+import xlogo.kernel.userspace.files.LogoFile;
+import xlogo.kernel.userspace.procedures.Procedure;
+import xlogo.storage.global.GlobalConfig;
+/**
+ * A LogoContext contains all the symbol tables for execution of Logo programs <p>
+ * Parts of this resemble the old Workspace class, but this has a new purpose and it is used differently.
+ * In XLogo4Schools, a LogoContext is only a container for a Logo environment - all the symbol tables.
+ * <p>
+ * Note: To be consistent, the execution stack should be included in the context too, but this would (for now) need too much work
+ * to refactor the existing interpreter. It is for sure something that should be done in the future, when the entire interpreter is refactored.
+ *
+ * @author Marko Zivkovic -
+ * @author Lo�c Le Coq - methods inherited from XLogo are marked with author-tag
+ */
+public class LogoContext
+{
+ private LogoFile openFile;
+
+ private final GlobalVariableTable globals = new GlobalVariableTable();
+
+ private final PropertyListTable propertyLists = new PropertyListTable();
+
+ private final Map<String, LogoFile> files = new HashMap<String, LogoFile>();
+
+ private final HashMap<String, HashMap<String, Procedure>> procedureTable = new HashMap<String, HashMap<String, Procedure>>();
+
+ // private Map<String, Procedure> executables = new HashMap<String,
+ // Procedure>();
+
+ private final GuiMap guiMap = new GuiMap();
+
+ public LogoContext() { }
+
+ /*
+ * Symbol table getters.
+ */
+
+ /**
+ * All the files in this context
+ * @return
+ */
+ public Map<String, LogoFile> getFilesTable()
+ {
+ return files;
+ }
+
+ /**
+ * All Executable procedures from all files. Procedures with equal names,
+ * from different files, will be included in the same list. <br>
+ * This Map is used to keep track of all defined procedures and to resolve
+ * name conflicts.
+ */
+ public HashMap<String, HashMap<String, Procedure>> getProcedureTable()
+ {
+ return procedureTable;
+ }
+
+ /**
+ * Global Logo variables
+ * @return
+ */
+
+ public GlobalVariableTable getGlobals()
+ {
+ return globals;
+ }
+
+ /**
+ * Logo property lists
+ * @return
+ */
+ public PropertyListTable getPropertyLists()
+ {
+ return propertyLists;
+ }
+
+ /**
+ * For all Gui Objects (Buttons, ComboBoxes...)
+ */
+ public GuiMap getGuiMap()
+ {
+ return guiMap;
+ }
+
+ /*
+ * Context dependent operations
+ */
+
+ public String[] getFileOrder()
+ {
+ return files.keySet().toArray(new String[files.size()]);
+ }
+
+ public void openFile(String fileName)
+ {
+ openFile = files.get(fileName);
+ }
+
+ public void closeFile()
+ {
+ openFile = null;
+ }
+
+ public LogoFile getOpenFile()
+ {
+ return openFile;
+ }
+
+ /**
+ * This is the preferred method to create a file within a context, because different contexts prefer different LogoFile configurations.
+ * @throws IllegalStateException if fileName already exists in the files table
+ * @throws IOException If the file could not be created on the file system and the text not written.
+ */
+ public void createFile(String fileName, String text) throws IOException
+ {
+ if (files.containsKey(fileName))
+ throw new IllegalStateException("Attempt to create already existing file.");
+
+ LogoFile file = LogoFile.createNewFile(fileName);
+ file.setText(text);
+ file.store();
+
+ files.put(fileName, file);
+ installListeners(file);
+ }
+
+ public void importFile(File path, String newFileName) throws IOException
+ {
+ if (files.containsKey(newFileName))
+ throw new IllegalStateException("Attempt to create already existing file.");
+
+ if (!path.isFile())
+ throw new IllegalArgumentException("The specified file does not exist : " + path.toString());
+
+ String extension = GlobalConfig.LOGO_FILE_EXTENSION;
+ if(!path.getName().endsWith(extension))
+ throw new IllegalArgumentException("Only accept " + extension + " files, but received : " + path.toString());
+
+ String fileName = path.getName();
+ fileName = path.getName().substring(0, fileName.length() - extension.length());
+
+ LogoFile file = LogoFile.importFile(path, newFileName);
+ files.put(newFileName, file);
+ installListeners(file);
+ }
+
+ /**
+ * Note: does not perform checks on validity of file names.
+ * @param oldName
+ * @param newName
+ */
+ public void renameFile(String oldName, String newName)
+ {
+ if (oldName.equals(newName))
+ return;
+
+ LogoFile file = files.get(oldName);
+
+ // must first re-map in files table because file.setFileName fires events
+ files.remove(oldName);
+ files.put(newName, file);
+
+ file.setFileName(newName);
+ }
+
+ private final ArrayList<ProcedureMapListener> procedureMapListener = new ArrayList<ProcedureMapListener>();
+
+ /**
+ * This should be used when a file is created or added to this context.
+ * Listeners that were previously added with {@link #addProcedureMapListener(ProcedureMapListener)} will be installed.
+ * @param file
+ */
+ public void installListeners(LogoFile file)
+ {
+ for (ProcedureMapListener listener : procedureMapListener)
+ file.addProcedureMapListener(listener);
+ }
+
+ /**
+ * This helps the Procedure Manager to register to ProcedureMap events from the logo files.
+ * The ProcedureManager does not need to be notified explicitly when a new file is created.
+ * @see #installListeners(LogoFile)
+ * @param listener
+ */
+ public void addProcedureMapListener(ProcedureMapListener listener)
+ {
+ procedureMapListener.add(listener);
+ for (LogoFile file : files.values())
+ file.addProcedureMapListener(listener);
+ }
+
+ public void removeProcedureMapListener(ProcedureMapListener listener)
+ {
+ procedureMapListener.remove(listener);
+ for (LogoFile file : files.values())
+ file.removeProcedureMapListener(listener);
+ }
+
+ /*
+ * MISC
+ */
+
+ /**
+ * That's the String that is sent via TCP, and interpreted by the receiver in {@link NetworkContext},
+ * using {@link #setWorkspace(String)}
+ * @author Lo�c Le Coq
+ * @author Marko Zivkovic
+ * refactored using the new data structures.
+ */
+ public String toString()
+ {
+ StringBuffer sb = new StringBuffer();
+
+ for (String key : getGlobals().getVariables())
+ {
+ sb.append("-");
+ sb.append(key);
+ sb.append("\n");
+ sb.append(getGlobals().getValue(key));
+ sb.append("\n");
+ }
+
+ for (HashMap<String, Procedure> fileToProc : procedureTable.values())
+ {
+ if (fileToProc.size() != 1)
+ continue;
+
+ Procedure procedure = null;
+ for (Procedure uniqueProc : fileToProc.values())
+ procedure = uniqueProc; // retrieve the only procedure in fileToProc
+
+ sb.append(Logo.messages.getString("pour") + " " + procedure.name);
+ for (int j = 0; j < procedure.nbparametre; j++)
+ {
+ sb.append(" :" + procedure.variable.get(j));
+ }
+ sb.append("\n");
+ sb.append(procedure.instruction);
+ sb.append(Logo.messages.getString("fin"));
+ sb.append("\n\n");
+ }
+ return (new String(sb));
+ }
+
+ /**
+ * @return true : {@link FileContainerChangeListener} should be fired.
+ */
+ public boolean fireFileEvents()
+ {
+ return true;
+ }
+
+ /**
+ * @return true : {@link AmbiguityListener},
+ * {@link ExecutablesChangedListener},
+ * {@link ProceduresDefinedListener} should be fired.
+ */
+ public boolean fireProcedureEvents()
+ {
+ return true;
+ }
+
+ /**
+ * Default is true. Other contexts might override this.
+ * @return Whether it is allowed to create, delete, or rename files.
+ * @note : this is only a suggestion for the files manager and the gui, modification of the table is still possible
+ */
+ public boolean isFilesListEditAllowed()
+ {
+ return true;
+ }
+}
diff --git a/logo/src/xlogo/kernel/userspace/context/NetworkContext.java b/logo/src/xlogo/kernel/userspace/context/NetworkContext.java new file mode 100644 index 0000000..937e513 --- /dev/null +++ b/logo/src/xlogo/kernel/userspace/context/NetworkContext.java @@ -0,0 +1,206 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program 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 2 of the License, or (at your option)
+ * any later version. This program is 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 program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace.context;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StringReader;
+
+import xlogo.Logo;
+import xlogo.kernel.userspace.files.LogoFile;
+
+/**
+ * The network context is very different compared to the other contexts. <br>
+ * 1. It parses its contents from a serialized context string ({@link #toString()}) <br>
+ * 2. Its files are created virtually ({@link xlogo.storage.Storable#isVirtual()}). They are gone when the network mode stops, leaving no trace. <br>
+ * 3. {@link #fireFileEvents()}} and {@link fireProcedureEvents()}} suggest the managers to not fire events,
+ * because the network files are not meant to be displayed in the editor.
+ * In XLogo this effect was achieved by setting the property affichable of procedures.
+ * @author Marko Zivkovic
+ */
+public class NetworkContext extends LogoContext
+{
+
+ public NetworkContext(String networkContext) throws IllegalArgumentException
+ {
+ super();
+ setContext(networkContext);
+ }
+
+ /**
+ * This is used to include a remote context into XLogo4Schools and allow remote
+ * procedure calls via tcp. XLogo had this implemented in the Workspace class.
+ * <p>
+ * Initially, in remote mode, the workspace was temporarily replaced by a new one.
+ * To indicate that the workspace with its procedures should not be displayed
+ * in the editor, Lo�c had added setAffichable(false), meaning not displayable.<br>
+ *
+ * Note that in XLogo, the workspace contained exactly one file. Therefore
+ * a simple swap in swap would do. However, in XLogo4Schools, we need more
+ * state information, and some of the state must be preserved.
+ * For example, in network mode, I do not want the user's procedures
+ * to be removed from the procedure list. I just want to disable the list.<br>
+ * <p>
+ * With this Network context, no events will be fired that would indicate,
+ * that some procedures or files have changed. But the ContextChangedListeners
+ * will be notified that we are now in network mode, thus they can disable
+ * the files list.
+ * <p>
+ * <p>
+ * Note that in XLogo, the workspace text has to be set in the editor just
+ * because analysProcedure() reads the Logo source code from there.
+ * In the network mode, It was actually not necessary to have it in the editor,
+ * because they didn't intend do display the text anyway (setAffichable(false)).
+ * setWorkspace and toString have probably been added late, so they just "hacked"
+ * that new property in.
+ * The existing "architecture" did not allow a cleaner extension of the
+ * system, so they must have added "affichable" for this only purpose.
+ * <p>
+ * In the current implementation, "affichable" is completely removed. <br>
+ * I regulate this effect by either firing events or not.
+ * <p>
+ * Note, parsing of the context is inherited from XLogo. Its implementation is very optimistic.
+ * It assumes that only well-formed strings are provided. Therefore it throws no actual exceptions.
+ * I added IllegalArgumentException to provide a more general interface,
+ * and to prepare clients for the future (?) when this parsing is re-implemented.
+ *
+ * @param app
+ * @param wp
+ * @author Marko Zivkovic, Loic
+ */
+ protected void setContext(String wp) throws IllegalArgumentException
+ {
+ StringReader sr = new StringReader(wp);
+ BufferedReader bfr = new BufferedReader(sr);
+ try
+ {
+ String input = parseVariables(bfr);
+
+ if (input != null)
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append(input);
+ sb.append("\n");
+ readerToBuilder(sb, bfr);
+
+ String vFileName = "netowork_file";
+ createFile(vFileName, "");
+ LogoFile file = getFilesTable().get(vFileName);
+ file.setText(sb.toString());
+ }
+ }
+ catch (IOException e)
+ {}
+ finally
+ {
+ try
+ {
+ bfr.close();
+ }
+ catch (IOException e)
+ {}
+ sr.close();
+ }
+ }
+
+ /**
+ * append all lines of br to sb
+ */
+ private void readerToBuilder(StringBuilder sb, BufferedReader br) throws IOException
+ {
+ String line = null;
+ while((line = br.readLine()) != null)
+ {
+ sb.append(line);
+ sb.append("\n");
+ }
+ sb.deleteCharAt(sb.length()-1);
+ }
+
+ private String parseVariables(BufferedReader bfr) throws IOException, IllegalArgumentException
+ {
+ String input = "";
+ while ((input = bfr.readLine()) != null)
+ {
+ if (!input.startsWith(Logo.messages.getString("pour")))
+ {
+ String var = input.substring(1); // - expected
+ String value = bfr.readLine();
+ getGlobals().define(var, value);
+ }
+ else
+ break;
+ }
+ return input;
+ }
+
+ /**
+ * Files created in network mode are virtual.
+ */
+ @Override
+ public void createFile(String fileName, String text) throws IllegalArgumentException
+ {
+ LogoFile vFile = LogoFile.createNewVirtualFile(fileName);
+ getFilesTable().put(fileName, vFile);
+
+ LogoFile file = LogoFile.createNewVirtualFile(fileName);
+ file.setText(text);
+
+ getFilesTable().put(fileName, file);
+
+ installListeners(file);
+ }
+
+ /**
+ * @return In network mode : false : {@link FileContainerChangeListener} events will not be fired.
+ */
+ public boolean fireFileEvents()
+ {
+ return false;
+ }
+
+ /**
+ * @return In network mode : false : {@link AmbiguityListener},
+ * {@link xlogo.interfaces.ExecutablesChangedListener},
+ * {@link xlogo.interfaces.ProceduresDefinedListener} events will not be fired.
+ */
+ public boolean fireProcedureEvents()
+ {
+ return false;
+ }
+
+ /**
+ * The Network context suggest that the user should not be allowed to create files from the gui.
+ * @see LogoContext#isFilesListEditAllowed()
+ */
+ @Override
+ public boolean isFilesListEditAllowed()
+ {
+ return false;
+ }
+}
diff --git a/logo/src/xlogo/kernel/userspace/context/RecordContext.java b/logo/src/xlogo/kernel/userspace/context/RecordContext.java new file mode 100644 index 0000000..48237de --- /dev/null +++ b/logo/src/xlogo/kernel/userspace/context/RecordContext.java @@ -0,0 +1,146 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program 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 2 of the License, or (at your option)
+ * any later version. This program is 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 program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace.context;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+import xlogo.interfaces.MessageBroadcaster;
+import xlogo.kernel.userspace.files.RecordFile;
+
+/**
+ * The context for contest/record mode.
+ * When creating a new RecordContext, a number of RecordFiles are created, for each name provided in the constructor
+ * <p>
+ * Note that a RecordContext only works correctly with RecordFiles.
+ * Therefore FileManagers should always use context.createFile(); to add new files, and never put them directly into the files table.
+ * <p>
+ * Besides, RecordContext suggests to not create new files ({@link #isFilesListEditAllowed()}),
+ * but files can still be created by using Logo commands, such as define or load.
+ *
+ * @author Marko
+ */
+public class RecordContext extends LogoContext implements MessageBroadcaster
+{
+ private String[] fileOrder;
+
+ private MessageListener fileTimerListener;
+ private ArrayList<MessageListener> timerEventListeners = new ArrayList<MessageListener>();
+
+ public RecordContext(final String[] recordModeFileNames) throws IOException
+ {
+ super();
+ this.fileOrder = recordModeFileNames;
+ initFileTimerListener();
+ setupRecordFiles();
+ }
+
+ protected void setupRecordFiles() throws IOException
+ {
+ for(String fileName : fileOrder)
+ createFile(fileName, "");
+ }
+
+ private void initFileTimerListener()
+ {
+ fileTimerListener = new MessageListener(){
+
+ @Override
+ public void messageEvent(String source, String message)
+ {
+ for (MessageListener listener : timerEventListeners)
+ listener.messageEvent(source, message);
+ }
+ };
+ }
+
+ @Override
+ public void createFile(String fileName, String text) throws IOException
+ {
+ RecordFile file = RecordFile.createNewFile(fileName);
+
+ if (text != null && text.length() > 0)
+ {
+ file.setText(text);
+ file.store();
+ }
+
+ installListeners(file);
+ file.addBroadcastListener(fileTimerListener);
+
+ getFilesTable().put(fileName, file);
+ }
+
+ public void openFile(String fileName)
+ {
+ super.openFile(fileName);
+ RecordFile file = (RecordFile) getFilesTable().get(fileName);
+ file.startRecord();
+ }
+
+ @Override
+ public void closeFile()
+ {
+ RecordFile file = (RecordFile) getOpenFile();
+ file.pauseRecord();
+ super.closeFile();
+ }
+
+ @Override
+ public String[] getFileOrder()
+ {
+ // TODO must extend file order for command "define" [otherwise constant number of files in record mode]
+ // - although it's not really correct, no visible consequences in app => no priority ... program works just as well
+ return fileOrder;
+ }
+
+ /**
+ * The Record context suggest that the user should not be allowed to create files from the gui.
+ * @see LogoContext#isFilesListEditAllowed()
+ */
+ @Override
+ public boolean isFilesListEditAllowed()
+ {
+ return false;
+ }
+
+
+ @Override
+ public void addBroadcastListener(MessageListener listener)
+ {
+ timerEventListeners.add(listener);
+ }
+
+ @Override
+ public void removeBroadcastListener(MessageListener listener)
+ {
+ timerEventListeners.add(listener);
+ }
+
+
+}
diff --git a/logo/src/xlogo/kernel/userspace/context/UserContext.java b/logo/src/xlogo/kernel/userspace/context/UserContext.java new file mode 100644 index 0000000..064e665 --- /dev/null +++ b/logo/src/xlogo/kernel/userspace/context/UserContext.java @@ -0,0 +1,191 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program 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 2 of the License, or (at your option)
+ * any later version. This program is 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 program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace.context;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+
+import xlogo.Logo;
+import xlogo.kernel.userspace.files.LogoFile;
+import xlogo.messages.MessageKeys;
+import xlogo.messages.async.dialog.DialogMessenger;
+import xlogo.storage.WSManager;
+import xlogo.storage.global.GlobalConfig;
+import xlogo.storage.user.UserConfig;
+
+public class UserContext extends LogoContext
+{
+
+ /**
+ * Load and parse all the files in current user's source directory. <br>
+ * This happens only once, in the constructor. <br>
+ * Refresh is currently not planned, but it would be easy to add.
+ *
+ * @param userDir
+ */
+ public UserContext()
+ {
+ loadUserFiles();
+ }
+
+ private void loadUserFiles()
+ {
+ UserConfig userConfig = WSManager.getUserConfig();
+
+ if (userConfig.isVirtual())
+ return;
+
+ File sourceDir = userConfig.getSourceDirectory();
+ if (!sourceDir.exists())
+ sourceDir.mkdirs();
+
+ if (!sourceDir.isDirectory())
+ {
+ DialogMessenger.getInstance().dispatchMessage(Logo.messages.getString("ws.error.title"),
+ Logo.messages.getString("ws.error.userdir.not.dir"));
+ return;
+ }
+
+ StringBuilder ioErrors = new StringBuilder();
+
+ for (String fileName : getLogoFileNamesFromDirectory(sourceDir))
+ {
+ String name = fileName.substring(0, fileName.length() - GlobalConfig.LOGO_FILE_EXTENSION.length());
+ userConfig.addFile(name);
+ try
+ {
+ LogoFile file = LogoFile.loadFile(name);
+ getFilesTable().put(file.getPlainName(), file);
+
+ }
+ catch (IOException e)
+ {
+ ioErrors.append(e.toString());
+ ioErrors.append("\n\n");
+ }
+ }
+
+ // must remove files from fileOrder that could not be found anymore.
+ for (String fileName : new ArrayList<String>(userConfig.getFileOrder()))
+ {
+ if (!getFilesTable().containsKey(fileName))
+ userConfig.getFileOrder().remove(fileName);
+ }
+
+ if (ioErrors.length() > 0)
+ {
+ DialogMessenger.getInstance().dispatchMessage(Logo.messages.getString("ws.error.title"),
+ Logo.messages.getString("ws.error.could.not.load.logo.files") + "\n" + ioErrors.toString());
+ }
+ }
+
+ /**
+ * Caller must make sure that newName does not already exist.
+ */
+ @Override
+ public void renameFile(String oldName, String newName)
+ {
+ super.renameFile(oldName, newName);
+ WSManager.getUserConfig().renameFile(oldName, newName);
+ }
+
+ @Override
+ public void createFile(String fileName, String text) throws IOException
+ {
+ /*
+ * Eager creation of files in file order list in user config.
+ */
+
+ if (!WSManager.getUserConfig().isVirtual())
+ super.createFile(fileName, text);
+ else
+ {
+ LogoFile file = LogoFile.createNewVirtualFile(fileName);
+ file.setText(text);
+ getFilesTable().put(fileName, file);
+ installListeners(file);
+ }
+ WSManager.getUserConfig().addFile(fileName);
+ }
+
+ @Override
+ public void importFile(File path, String newFileName)
+ {
+ try
+ {
+ super.importFile(path, newFileName);
+ }
+ catch (IOException e)
+ {
+ DialogMessenger.getInstance().dispatchError(MessageKeys.GENERAL_ERROR_TITLE, "Could not import file : \n" + e.toString());
+ }
+ WSManager.getUserConfig().addFile(newFileName);
+ }
+
+ private String[] getLogoFileNamesFromDirectory(File dir)
+ {
+ return dir.list(new FilenameFilter(){
+ public boolean accept(File file, String name)
+ {
+ return name.endsWith(".lgo");
+ }
+ });
+ }
+
+ @Override
+ public String[] getFileOrder()
+ {
+ /*
+ * Lazy deletion from file order list in user config.
+ */
+ ArrayList<String> list = new ArrayList<String>(WSManager.getUserConfig().getFileOrder());
+ Map<String,LogoFile> filesTable = getFilesTable();
+
+ if (filesTable.size() != list.size())
+ {
+ Iterator<String> iter = list.iterator();
+ String current;
+
+ while(iter.hasNext())
+ {
+ current = iter.next();
+ if(!filesTable.containsKey(current))
+ iter.remove();
+ }
+ }
+
+ return list.toArray(new String[list.size()]);
+ }
+
+
+
+}
diff --git a/logo/src/xlogo/kernel/userspace/files/LogoFile.java b/logo/src/xlogo/kernel/userspace/files/LogoFile.java new file mode 100644 index 0000000..92fe23c --- /dev/null +++ b/logo/src/xlogo/kernel/userspace/files/LogoFile.java @@ -0,0 +1,755 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program 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 2 of the License, or (at your option)
+ * any later version. This program is 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 program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace.files;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+
+import xlogo.Logo;
+import xlogo.interfaces.ErrorDetector;
+import xlogo.interfaces.ProcedureMapper;
+import xlogo.kernel.userspace.ProcedureErrorMessage;
+import xlogo.kernel.userspace.procedures.ExecutablesContainer;
+import xlogo.kernel.userspace.procedures.Procedure;
+import xlogo.kernel.userspace.procedures.Procedure.State;
+import xlogo.messages.MessageKeys;
+import xlogo.messages.async.dialog.DialogMessenger;
+import xlogo.storage.Storable;
+import xlogo.storage.StorableDocument;
+import xlogo.storage.WSManager;
+import xlogo.storage.global.GlobalConfig;
+import xlogo.storage.user.UserConfig;
+import xlogo.storage.workspace.NumberOfBackups;
+import xlogo.storage.workspace.WorkspaceConfig;
+import xlogo.utils.Utils;
+
+/**
+ * This class holds the text file a user entered in the editor.
+ * It analyzes the text and maintains a symbol table for all defined procedures that live within it.
+ * <p>
+ * The file does never store itself implicitly, except for when it is created using {@link #createNewFile(String)} or renamed using {@link #setFileName(String)}
+ * In every other case, {@link #store()}} or {@link #storeCopyToFile(File)}} must be invoked explicitly.
+ * <p>
+ * The file's text can be set using {@link #setTextFromReader(BufferedReader)}} (preferred) or {@link #setText(String)}}.
+ * Both will try to parse the signature of all procedures using the constructor of {@link xlogo.kernel.userspace.procedures.Procedure}
+ *
+ * @author Marko Zivkovic, (Lo�c Le Coq's parsing of procedures is not recognizable anymore.)
+ *
+ */
+public class LogoFile extends StorableDocument implements ExecutablesContainer, ProcedureMapper, ErrorDetector
+{
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1117062836862782516L;
+
+ /**
+ * UserConfig of the owner of this file
+ */
+ private UserConfig userConfig;
+
+ /**
+ * Contains only executable procedures
+ */
+ private Map<String, Procedure> executables;
+
+ /**
+ * Contains all procedures, no matter what the state is.
+ * The order of the list is relevant to reproduce the editor text after the Logo command 'eraseprocedure'
+ * (after {@link #deleteProcedure(String)}})
+ */
+ private ArrayList<Procedure> allProcedures;
+
+ /**
+ * A flag that indicated whether the last parsing ended with errors or ambiguities
+ */
+ private boolean hasError;
+
+ /*
+ * CONSTRUCTOR & STATIC CONSTRUCTORS, FILE LOADERS
+ */
+
+ /**
+ * The LogoFile automatically sets its location to the current user's src directory, if that user is not virtual.
+ * @param fileName
+ * @throws IllegalArgumentException see : {@link Storable#setFileName()}
+ */
+ protected LogoFile(String fileName) throws IllegalArgumentException
+ {
+ super();
+ this.userConfig = WSManager.getUserConfig();
+ if (!userConfig.isVirtual())
+ setLocation(userConfig.getSourceDirectory());
+ setFileName(fileName);
+ executables = new HashMap<String, Procedure>();
+ allProcedures = new ArrayList<Procedure>();
+ }
+
+ public static LogoFile createNewVirtualFile(String fileName)
+ {
+ LogoFile file = null;
+ try
+ {
+ file = new LogoFile(fileName);
+ file.makeVirtual();
+ }
+ catch (IllegalArgumentException ignore) { }
+ return file;
+ }
+ /**
+ * Create a new file and store it in the user's source directory.
+ * @throws IOException
+ * @throws IllegalArgumentException
+ */
+ public static LogoFile createNewFile(String fileName) throws IOException, IllegalArgumentException
+ {
+ LogoFile file = new LogoFile(fileName);
+ file.setupFileSystem();
+ return file;
+ }
+
+ /**
+ * Load the specified file from the user's source directory and parse procedure structures.
+ * @param fileName - without extension
+ * @return
+ * @throws IOException
+ */
+ public static LogoFile loadFile(String fileName) throws IOException
+ {
+ UserConfig userConfig = WSManager.getUserConfig();
+ File path = userConfig.getLogoFilePath(fileName);
+ String text = Utils.readLogoFile(path.toString());
+ LogoFile file = new LogoFile(fileName);
+ file.setText(text);
+ if (userConfig.isVirtual())
+ file.makeVirtual();
+ return file;
+ }
+
+ /**
+ * Open any file on the file system and integrate it in the UserSpace.
+ * The file will be stored and made visible under the specified newFileName
+ * @param file
+ * @param newFileName
+ * @throws IOException
+ */
+ public static LogoFile importFile(File path, String newFileName) throws IOException
+ {
+ String text = Utils.readLogoFile(path.toString());
+ LogoFile file = new LogoFile(newFileName);
+ file.setText(text);
+ if (WSManager.getUserConfig().isVirtual())
+ file.makeVirtual();
+ file.store();
+ return file;
+ }
+
+ protected UserConfig getUserConfig()
+ {
+ return userConfig;
+ }
+ /**
+ * This assumes that the file name is well formed. No additional checks are performed
+ * Rename this LogoFile and the file on the file system, if it exists there. Notify all FileChangeListeners.
+ * This accepts name with or without .lgo extension.
+ * @param newFileName - without extension
+ */
+ @Override
+ public void setFileName(String newFileName)
+ {
+ if (newFileName == null || newFileName.length() == 0)
+ {
+ DialogMessenger.getInstance().dispatchError(
+ Logo.messages.getString(MessageKeys.NAME_ERROR_TITLE),
+ Logo.messages.getString(MessageKeys.EMPTY_NAME));
+ return;
+ }
+
+ if (!Storable.checkLegalName(newFileName))
+ {
+ DialogMessenger.getInstance().dispatchError(
+ Logo.messages.getString(MessageKeys.NAME_ERROR_TITLE),
+ Logo.messages.getString(MessageKeys.ILLEGAL_NAME) + " : " + newFileName);
+ return;
+ }
+
+ String oldPlainName = getPlainName();
+ super.setFileName(newFileName);
+ String newPlainName = getPlainName();
+
+ if (oldPlainName != null)
+ notifyRenamed(oldPlainName, newPlainName);
+ }
+
+ @Override
+ public String getFileNameExtension()
+ {
+ return GlobalConfig.LOGO_FILE_EXTENSION;
+ }
+
+ private void notifyRenamed(String oldName, String newName)
+ {
+ for(ProcedureMapListener listener : procedureMapListeners)
+ listener.ownerRenamed(oldName, newName);
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * STORE & LOAD FILE
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ /**
+ * If this is not virtual, create this file on the file system and create a backup folder for it.
+ * @throws IOException
+ */
+ protected void setupFileSystem() throws IOException
+ {
+ if (isVirtual())
+ return;
+
+ File source = getFilePath();
+ File backupFolder = userConfig.getFileBackupDir(getPlainName());
+
+ if (!source.getParentFile().exists())
+ source.getParentFile().mkdirs();
+
+ if (!backupFolder.exists())
+ backupFolder.mkdirs();
+
+ storeCopyToFile(source);
+ }
+
+ /**
+ * If this is not virtual, store the file in the source folder of the UserSpace, <br>
+ * and another copy in the backup folder, if this is required by {@link WorkspaceConfig#getNumberOfBackups()}.
+ */
+ @Override
+ public void store() throws IOException
+ {
+ super.store();
+ if (isVirtual())
+ return;
+ doBackup();
+ }
+
+ @Override
+ public void delete()
+ {
+ super.delete();
+ Collection<String> procedures = new ArrayList<String>(executables.keySet());
+ executables.clear();
+ allProcedures.clear();
+ notifyDeleted(procedures);
+ }
+
+
+ /**
+ * Store a backup copy of this file.
+ * If the number of maximally allowed backups is exceeded,
+ * delete the oldest copies until the number of backups equals the limit
+ * defined by {@link WorkspaceConfig#getNumberOfBackups()}}
+ * @throws IOException
+ */
+ private void doBackup() throws IOException
+ {
+ WorkspaceConfig wc = WSManager.getInstance().getWorkspaceConfigInstance();
+ NumberOfBackups nob = wc.getNumberOfBackups();
+
+ File backupFile = userConfig.getBackupFilePath(getPlainName());
+ File backupFolder = backupFile.getParentFile();
+ if (!backupFolder.exists())
+ backupFolder.mkdirs();
+
+ if (nob != NumberOfBackups.NO_BACKUPS)
+ storeCopyToFile(backupFile);
+
+ if (nob == NumberOfBackups.INFINITE)
+ return;
+
+ int max = nob.getNumber(); // max is >= 0
+ // Assume no outer manipulation of that directory
+ File[] backups = backupFolder.listFiles();
+
+ int actual = backups.length;
+ if (actual <= max)
+ return;
+
+ // must delete the oldest backups
+ Arrays.sort(backups, new Comparator<File>(){
+ public int compare(File f1, File f2)
+ {
+ return f2.getName().compareTo(f1.getName().toString());
+ }
+ });
+
+ while (actual > max)
+ {
+ actual--;
+ backups[actual].delete();
+ }
+ }
+
+ /**
+ * The file path of this LogoFile in the source directory.
+ */
+ @Override
+ public File getFilePath()
+ {
+ if (super.getFilePath() != null)
+ return super.getFilePath();
+ return userConfig.getLogoFilePath(getPlainName());
+ }
+
+ @Override
+ public File getLocation()
+ {
+ if (super.getLocation() != null)
+ return super.getLocation();
+ return userConfig.getSourceDirectory();
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * SERIALIZATION AND DESERIALIZATION
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ @Override
+ protected String generateText()
+ {
+ StringBuilder text = new StringBuilder();
+
+ for(Procedure proc : allProcedures)
+ {
+ text.append(proc.getText());
+ text.append("\n");
+ }
+
+ return text.toString();
+ }
+
+ /**
+ * Changes made 21.6.2013 and July 2013
+ * <p>
+ * In XLogo, this was {@code Editor.analyseprocedure()}
+ * <p><p>
+ * <b>Refactored:</b><br>
+ * Initially all that happens in
+ * <li> {@link #setText(String)}
+ * <li> {@link #parseText(BufferedReader)}
+ * <li> {@link #nextProcedure(BufferedReader)}
+ * <li> {@link Procedure#Procedure() }
+ * <li> {@link #untilEnd() }<br>
+ * was composed into one big and unreadable procedure {@code Editor.analyseprocedure()}.
+ * Note that the Editor (a GUI Controller) was responsible to parse text.
+ * {@code Editor.analyseprocedure()} took the text it was analyzing from the Editor's text component directly.
+ * Thus, whenever a procedure was programmatically defined, or if a workspace was received from the network,
+ * the text had to be written to the editor first before {@code Editor.analyseprocedure()} was called.<p>
+ * Note that in the networking case, the received text was never meant to be displayed.
+ * In that case the Editor served merely as a temporary container such that analyseProcedure() could read the text from it.
+ * This was the only reason why the property "affichable" (displayable) was added to so many classes.
+ *
+ * <p><p>
+ * <b>New Mechanism:</b><br>
+ * In XLogo, as soon as an error was found in the document, an exception was thrown and displayed to the user. <br>
+ * The new approach is to first split the document wherever a line starts with a token 'end'.
+ * <p>
+ * [#belongs to procedure 1<br>
+ * ... <br>
+ * end][#belongs to procedure 2 <br>
+ * ... <br>
+ * end] ...
+ * <p>
+ * These parts of the document are given to the constructor {@code Procedure#Procedure(String)},
+ * so the procedure can maintain its own state
+ * <br>
+ * Based on the type of errors, a Procedure can now detect several errors at a time and report them.
+ * The LogoFile can then report all errors that have been collected from its procedures.
+ * This approach allows to give more precise error messages to the user.
+ * Example: It is now possible to say which procedure is missing an 'end'
+ * <br>
+ * In the new implementation, a Procedure is not necessarily executable.
+ * Whether it is executable, can be read from its state {@link Procedure.State}.
+ * Its state can be
+ * <li> UNINITIALIZED
+ * <li> EXECUTABLE
+ * <li> COMMENT_ONLY (for white space and comments at the end of the document)
+ * <li> ERROR
+ * <li> AMBIGUOUS_NAME <br>
+ * <p>
+ * Only EXECUTABLE procedures are included in the procedureTable of the {@link xlogo.kernel.userspace.context.LogoContext},
+ * but all procedures are maintained by LogoFile.
+ * @param str
+ * @throws DocumentStructureException
+ * @throws IOException
+ */
+ @Override
+ protected void parseText(BufferedReader br)
+ {
+ /*
+ * Keep old procedures before reset of procedure tables.
+ * procedures that remain in the end, will count as deleted.
+ * procedures that existed before, but have errors now, count as deleted.
+ */
+ HashMap<String, Void> deleted = new HashMap<String, Void>();
+ for (Procedure proc : executables.values())
+ deleted.put(proc.getName(), null);
+
+ /*
+ * Must notify that all old executables are deleted as soon as a single procedure has an error,
+ * We want the whole file to be not executable when there exists an error
+ */
+ Collection<String> oldExecutables = new ArrayList<String>(executables.keySet());
+
+ /*
+ * We don't want the procedures to become ordered by creation time in the editor.
+ * The Logo command "define" was affected by this change, hence it was adapted to work as before.
+ * <p>
+ * Because we delete all the procedures from the tables [unlike XLogo] every time before reading the file,
+ * the procedures will be stored in the order in which the user defined them last.
+ */
+ resetProcedureTables(); // Added by Marko Zivkovic, 21.6.2013
+
+ // When the file is empty, it has no errors...
+ hasError = false;
+
+ try
+ {
+ while (br.ready()) // next procedure
+ {
+ Procedure proc;
+ String procedureText = untilEnd(br);
+ if (procedureText.equals(""))
+ break;
+ proc = new Procedure(procedureText);
+ proc.setOwnerName(getPlainName());
+
+ if (proc.getState() == State.EXECUTABLE)
+ {
+ deleted.remove(proc.getName());
+ }
+ addProcedure(proc);
+
+ if(proc.getState() == State.ERROR || proc.getState() == State.AMBIGUOUS_NAME)
+ hasError = true;
+ }
+ }
+ catch (IOException e){} // This should not happen, because no actual IO happens
+ finally { try { br.close(); } catch (IOException e) { } }
+
+ if(hasError)
+ {
+ notifyDeleted(oldExecutables);
+ return;
+ }
+
+ if (deleted.size() > 0)
+ notifyDeleted(deleted.keySet());
+
+ if (executables.size() > 0)
+ notifyDefined(executables.keySet());
+ }
+
+ /**
+ * @return String until the token 'end' is found on a line, or until the end of the BufferedReader
+ * @throws IOException
+ */
+ private static String untilEnd(BufferedReader br) throws IOException
+ {
+ String end = Logo.messages.getString("fin").toLowerCase();
+ StringBuffer text = new StringBuffer();
+ String line;
+
+ while (br.ready())
+ {
+ line = br.readLine();
+ if (line == null)
+ break;
+ else if (line.trim().toLowerCase().equals(end))
+ {
+ text.append(end);
+ break;
+ }
+ else
+ text.append(line + "\n");
+ }
+
+ return text.toString();
+ }
+
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * PROCEDURE CONTAINER
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ /**
+ * Note: does not notify <br>
+ * Delete all procedures
+ */
+ private void resetProcedureTables()
+ {
+ allProcedures.clear();
+ executables.clear();
+ invalidateText();
+ setText(null);
+ }
+
+ /**
+ * Implementation of the Logo Command "define". <br>
+ * FileChangeListeners are notified.
+ * <p>
+ * In XLogo, the command define had no effect, when an error was detected while parsing.
+ * The same is true here, because an IllegalStateException is thrown if procedure is not executable.
+ * <p>
+ * In XLogo4Schools, to preserve semantics, we create a {@link Procedure} using its normal constructor and then check for errors.
+ * If errors exist in the procedure text, the procedure should not be defined in its destined file either.
+ * The responsibility whether a procedure is added to a file lies therefore in the interpreter.
+ * <p>
+ * Existing procedures with the same name are just redefined, as in XLogo.
+ * <p>
+ * @param procedure Expects an executable procedure.
+ * @throws IllegalStateException - if procedure is not Executable or its name is ambiguous in this file.
+ */
+ @Override
+ public void defineProcedure(Procedure procedure)
+ {
+ if (procedure.getState() != State.EXECUTABLE)
+ throw new IllegalStateException("Attempt to define procedure which is not executable.");
+
+ Procedure other = executables.get(procedure.name);
+
+ invalidateText();
+
+ if (other != null)
+ {
+ if (other.getState() == State.AMBIGUOUS_NAME)
+ throw new IllegalStateException("Attempt to redefine ambiguous procedure.");
+
+ other.redefine(procedure);
+
+ }else
+ {
+ allProcedures.add(procedure);
+ executables.put(procedure.name, procedure);
+ }
+ notifyDefined(procedure.getName());
+ }
+
+ /**
+ * This is for the Logo command 'eraseprocedure'
+ * @param name
+ * @throws IllegalArgumentException
+ */
+ @Override
+ public void eraseProcedure(String name)
+ {
+ Procedure proc = getExecutable(name);
+ if(proc == null)
+ throw new IllegalStateException("Attempt to erase procedure which exists not.");
+ allProcedures.remove(proc);
+ executables.remove(name);
+ invalidateText();
+ notifyDeleted(proc.getName());
+ }
+
+ /**
+ * Note: Does not notify listeners! <br>
+ * Semantics: If more than one procedures with the same name are defined in a document,
+ * all are marked ambiguous. The first one is kept in the executables list to track ambiguity.
+ * @param pr
+ */
+ protected void addProcedure(Procedure pr)
+ {
+ if (pr.getState() == State.EXECUTABLE)
+ {
+ Procedure other = executables.get(pr.name);
+
+ if(other != null)
+ {
+ other.makeAmbiguous();
+ pr.makeAmbiguous();
+ }
+ else
+ executables.put(pr.name, pr);
+ }
+ allProcedures.add(pr);
+ invalidateText();
+ }
+
+ @Override
+ public Procedure getExecutable(String name)
+ {
+ return executables.get(name);
+ }
+
+ /**
+ * @param name
+ * @return Whether an executable procedure with the specified name exists
+ */
+ @Override
+ public boolean isExecutable(String name)
+ {
+ return executables.get(name) != null;
+ }
+
+ @Override
+ public Collection<Procedure> getExecutables()
+ {
+ if (hasErrors())
+ return new ArrayList<Procedure>();
+ return executables.values();
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * ERROR DETECTOR
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ @Override
+ public boolean hasErrors()
+ {
+ return hasError;
+ }
+
+ public Collection<ProcedureErrorMessage> getAllErrors()
+ {
+ ArrayList<ProcedureErrorMessage> allErrors = new ArrayList<ProcedureErrorMessage>();
+ for(Procedure proc : allProcedures)
+ {
+ for (xlogo.kernel.userspace.procedures.ProcedureErrorType e : proc.getErrors())
+ {
+ String description = proc.getName();
+ if (description == null)
+ {
+ description = proc.getText().length() < 100 ?
+ proc.getText() :
+ proc.getText().substring(0, 100) + "...";
+ }
+
+ allErrors.add(new ProcedureErrorMessage(e, description, getPlainName()));
+ }
+ }
+ return allErrors;
+ }
+
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * PROCEDURE MAPPER
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ /**
+ * Only executables.
+ * If the file has errors, no procedure is returned.
+ */
+ @Override
+ public Collection<String> getAllProcedureNames()
+ {
+ if (hasErrors())
+ return new ArrayList<String>();
+
+ ArrayList<String> procedureNames = new ArrayList<String>();
+
+ for (Procedure p : executables.values())
+ procedureNames.add(p.getName());
+
+ return procedureNames;
+ }
+
+ @Override
+ public Collection<String> getAllProcedureNames(String fileName)
+ {
+ if (fileName.equals(getPlainName()))
+ return getAllProcedureNames();
+ return null;
+ }
+
+ /**
+ * Behaves similar like contains(). If the procedure is in the file's executable list, then returns this file's plainName. Otherwise null.
+ */
+ @Override
+ public String getProcedureOwner(String procedureName)
+ {
+ if (executables.containsKey(procedureName))
+ return getPlainName();
+ return null;
+ }
+
+ // Procedure Map Listeners
+
+ private final ArrayList<ProcedureMapListener> procedureMapListeners = new ArrayList<ProcedureMapListener>();
+
+ @Override
+ public void addProcedureMapListener(ProcedureMapListener listener)
+ {
+ procedureMapListeners.add(listener);
+
+ if(executables.size() > 0)
+ notifyDefined(executables.keySet()); // TODO hmmm
+ }
+
+ @Override
+ public void removeProcedureMapListener(ProcedureMapListener listener)
+ {
+ procedureMapListeners.remove(listener);
+ }
+
+ protected void notifyDefined(Collection<String> procedures)
+ {
+ for (ProcedureMapListener listener : procedureMapListeners)
+ listener.defined(getPlainName(), procedures);
+ }
+
+ protected void notifyDefined(String procedure)
+ {
+ for (ProcedureMapListener listener : procedureMapListeners)
+ listener.defined(getPlainName(), procedure);
+ }
+
+ protected void notifyDeleted(Collection<String> collection)
+ {
+ for (ProcedureMapListener listener : procedureMapListeners)
+ listener.undefined(getPlainName(), collection);
+ }
+
+ protected void notifyDeleted(String procedure)
+ {
+ for (ProcedureMapListener listener : procedureMapListeners)
+ listener.undefined(getPlainName(), procedure);
+ }
+
+
+}
diff --git a/logo/src/xlogo/kernel/userspace/files/LogoFileContainer.java b/logo/src/xlogo/kernel/userspace/files/LogoFileContainer.java new file mode 100644 index 0000000..9c077a9 --- /dev/null +++ b/logo/src/xlogo/kernel/userspace/files/LogoFileContainer.java @@ -0,0 +1,39 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program 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 2 of the License, or (at your option)
+ * any later version. This program is 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 program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace.files;
+
+import xlogo.interfaces.BasicFileContainer;
+
+
+public interface LogoFileContainer extends BasicFileContainer
+{
+ /**
+ * Logo Command implementation
+ */
+ public void editAll();
+}
diff --git a/logo/src/xlogo/kernel/userspace/files/LogoFilesManager.java b/logo/src/xlogo/kernel/userspace/files/LogoFilesManager.java new file mode 100644 index 0000000..7a54902 --- /dev/null +++ b/logo/src/xlogo/kernel/userspace/files/LogoFilesManager.java @@ -0,0 +1,560 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program 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 2 of the License, or (at your option)
+ * any later version. This program is 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 program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace.files;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collection;
+
+import xlogo.Logo;
+import xlogo.interfaces.ErrorDetector.FileErrorCollector;
+import xlogo.kernel.userspace.ProcedureErrorMessage;
+import xlogo.kernel.userspace.context.ContextSwitcher;
+import xlogo.kernel.userspace.context.LogoContext;
+import xlogo.kernel.userspace.context.ContextSwitcher.ContextSwitchListener;
+import xlogo.messages.MessageKeys;
+import xlogo.messages.async.dialog.DialogMessenger;
+import xlogo.storage.Storable;
+import xlogo.storage.global.GlobalConfig;
+
+/**
+ * This Manager is completely new, because XLogo did not support multiple files. <br>
+ * During the requirements analysis, we have decided to maintain a global scope for procedures.
+ * That means a procedure defined in file A is visible in file B.
+ * <p>
+ * If we find during testing that the global scope is confusing for children and it leads to many ambiguity conflicts,
+ * then the current architecture allows to easily switch to file-wide scope. Instead of retrieving executables from the context's procedure table,
+ * we can directly retrieve them from the currently open/active file.
+ *
+ * @author Marko Zivkovic
+ */
+public class LogoFilesManager implements LogoFileContainer, FileErrorCollector
+{
+ private final ContextSwitcher contextProvider;
+ private LogoContext context;
+
+ private final ArrayList<FileContainerChangeListener> fileListeners = new ArrayList<FileContainerChangeListener>();
+
+ public LogoFilesManager(ContextSwitcher contextProvider)
+ {
+ this.contextProvider = contextProvider;
+ initContextSwitchListener();
+ setContext(contextProvider.getContext());
+ }
+
+ private void initContextSwitchListener()
+ {
+ contextProvider.addContextSwitchListener(new ContextSwitchListener(){
+ @Override
+ public void contextSwitched(LogoContext newContext)
+ {
+ setContext(newContext);
+ }
+ });
+ }
+
+ private void setContext(LogoContext newContext)
+ {
+ LogoContext old = context;
+ context = newContext;
+
+ LogoFile openFile = newContext.getOpenFile();
+ if (openFile != null)
+ closeFile(openFile.getPlainName());
+
+ if (newContext.fireFileEvents()) // Example : Network context does not change GUI, only internal change => no events
+ {
+ if (old != null && old.fireFileEvents())
+ for(LogoFile file : old.getFilesTable().values())
+ notifyFileRemoved(file.getPlainName());
+
+ for (String fileName : newContext.getFileOrder())
+ {
+ notifyFileAdded(fileName);
+ if (context.getFilesTable().get(fileName).hasErrors())
+ notifyErrorsDetected(fileName);
+ }
+ }
+
+ if (old == null || old.isFilesListEditAllowed() != newContext.isFilesListEditAllowed())
+ notifyRightsChanged();
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * X4S Specific features and Logo command implementations
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ /**
+ * The implementation of the Logo command {@code editall} or {@code edall} <br>
+ * In XLogo4Schools, we cannot open all files simultaneously to show all procedures. Instead, editall opens the file that was edited last.
+ */
+ public void editAll()
+ {
+ String fileName = getLastEditedFileName();
+ if (fileName == null)
+ return;
+ openFile(fileName);
+ }
+
+ @Override
+ public void importFile(File filePath) throws IOException
+ {
+ String name = filePath.getName().substring(0,
+ filePath.getName().length()
+ - GlobalConfig.LOGO_FILE_EXTENSION.length());
+
+ if(existsFile(name))
+ name = makeUniqueFileName(name);
+ context.importFile(filePath, name);
+ notifyFileAdded(name);
+ }
+
+ /**
+ * If file is a directory, the exported file will be named fileName.
+ * Otherwise the Logo-file will be exported to the file specified by dest
+ * @param fileName
+ * @param dest
+ * @throws IOException
+ */
+ public void exportFile(String fileName, File dest) throws IOException
+ {
+
+ if (dest.isDirectory())
+ exportFile(fileName, dest, fileName);
+ else
+ {
+ File parent = dest.getParentFile();
+ String targetName = dest.getName();
+ exportFile(fileName, parent, targetName);
+ }
+ }
+
+ /**
+ * @param fileName - of a file in the current context
+ * @param location - an existing directory on the file system
+ * @param targetName - the exported file's name
+ * @throws IOException
+ */
+ public void exportFile(String fileName, File location, String targetName) throws IOException
+ {
+ LogoFile file = context.getFilesTable().get(fileName);
+
+ if(file == null)
+ throw new IllegalArgumentException("The specified fileName does not exist in the context.");
+
+ if (!location.isDirectory())
+ throw new IllegalArgumentException("The specified location does not exist : " + location.toString());
+
+ String extendedName = targetName;
+
+ if(extendedName == null || extendedName.length() == 0)
+ extendedName = fileName;
+
+ String extension = GlobalConfig.LOGO_FILE_EXTENSION;
+
+ if(!extendedName.endsWith(extension))
+ extendedName += extension;
+
+ File target = new File(location.toString() + File.separator + extendedName);
+ file.storeCopyToFile(target);
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * LOGO FILE CONTAINER
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ @Override
+ public String[] getFileNames()
+ {
+ return context.getFileOrder();
+ }
+
+ @Override
+ public void createFile(String fileName) throws IOException
+ {
+ context.createFile(fileName, "");
+ notifyFileAdded(fileName);
+ }
+
+ @Override
+ public void writeFileText(String fileName, String content)
+ {
+ LogoFile file = context.getFilesTable().get(fileName);
+
+ if (file == null)
+ throw new IllegalStateException("Attempt to write to inexistent file.");
+
+ boolean hadErrors = file.hasErrors();
+
+ file.setText(content);
+
+ if (file.hasErrors())
+ notifyErrorsDetected(fileName); // notify anyway
+ else if (hadErrors)
+ notifyErrorsCorrected(fileName);
+ }
+
+ @Override
+ public void storeFile(String fileName) throws IOException
+ {
+ context.getFilesTable().get(fileName).store();
+ }
+
+ /**
+ * The file is also deleted from the file system
+ */
+ @Override
+ public void removeFile(String fileName)
+ {
+ LogoFile file = context.getFilesTable().get(fileName);
+ file.delete();
+ context.getFilesTable().remove(fileName);
+ notifyFileRemoved(fileName);
+ }
+
+ /**
+ * Deletes all files from the context and removes them from the context's tables. <br>
+ * Note: The events caused be deleting the files should cause all the procedures to disappear from the tables as well.
+ * [But the files manager doesn't care about procedures]
+ */
+ @Override
+ public void eraseAll()
+ {
+ Collection<LogoFile> files = context.getFilesTable().values();
+
+ while (!files.isEmpty())
+ {
+ LogoFile nextVictim = null;
+ for (LogoFile file : files)
+ {
+ nextVictim = file;
+ break;
+ }
+ nextVictim.delete();
+ context.getFilesTable().remove(nextVictim.getPlainName());
+ notifyFileRemoved(nextVictim.getPlainName());
+ }
+ context.getFilesTable().clear();
+ }
+
+ @Override
+ public boolean existsFile(String name)
+ {
+ return context.getFilesTable().containsKey(name);
+ }
+
+ @Override
+ public String readFile(String name)
+ {
+ return context.getFilesTable().get(name).getText();
+ }
+
+ /**
+ * Please make sure the renaming makes sense, otherwise an IllegalStateException is thrown at you.
+ */
+ @Override
+ public void renameFile(String oldName, String newName)
+ {
+ if (oldName.equals(newName))
+ return;
+
+ if(!existsFile(oldName))
+ {
+ DialogMessenger.getInstance().dispatchError(
+ Logo.messages.getString(MessageKeys.NAME_ERROR_TITLE),
+ Logo.messages.getString(MessageKeys.RENAME_INEXISTENT_FILE));
+ return;
+ }
+
+ if (existsFile(newName))
+ {
+ DialogMessenger.getInstance().dispatchError(
+ Logo.messages.getString(MessageKeys.NAME_ERROR_TITLE),
+ Logo.messages.getString(MessageKeys.WS_FILENAME_EXISTS_ALREADY));
+ return;
+ }
+
+ if (newName == null || newName.length() == 0)
+ {
+ DialogMessenger.getInstance().dispatchError(
+ Logo.messages.getString(MessageKeys.NAME_ERROR_TITLE),
+ Logo.messages.getString(MessageKeys.EMPTY_NAME));
+ return;
+ }
+
+ if (!Storable.checkLegalName(newName))
+ {
+ DialogMessenger.getInstance().dispatchError(
+ Logo.messages.getString(MessageKeys.NAME_ERROR_TITLE),
+ Logo.messages.getString(MessageKeys.ILLEGAL_NAME) + " : " + newName);
+ return;
+ }
+
+ context.renameFile(oldName, newName);
+ notifyFileRenamed(oldName, newName);
+ }
+
+ @Override
+ public String makeUniqueFileName(String base)
+ {
+ int i = 0;
+ String name = null;
+ do
+ {
+ name = base + i;
+ i++;
+ } while (existsFile(name));
+ return name;
+ }
+
+ /**
+ * @throws IllegalArgumentException if the specified file does not exist in the current context.
+ */
+ @Override
+ public void openFile(String fileName)
+ {
+ if(!existsFile(fileName))
+ throw new IllegalStateException("The specified file to open does not exist in the current context.");
+
+ LogoFile openFile = context.getOpenFile();
+ if(openFile != null)
+ closeFile(openFile.getPlainName());
+
+ context.openFile(fileName);
+ notifyFileOpened(fileName);
+ }
+
+ /**
+ * This can handle only one open file.
+ * If the wrong filename is closed, nothing happens<p>
+ * @throws IllegalStateException
+ */
+ @Override
+ public void closeFile(String fileName)
+ {
+ LogoFile openFile = context.getOpenFile();
+ if (openFile == null || !openFile.getPlainName().equals(fileName))
+ throw new IllegalStateException("Attempting to close a file that was not opened.");
+ context.closeFile();
+ notifyFileClosed(openFile.getPlainName());
+ }
+
+ /**
+ * returns null if no file is open.
+ */
+ @Override
+ public String getOpenFileName()
+ {
+ LogoFile file = context.getOpenFile();
+ if (file == null)
+ return null;
+ return file.getPlainName();
+ }
+
+ public boolean isFilesListEditable()
+ {
+ return context.isFilesListEditAllowed();
+ }
+
+ /**
+ * the name of the file that was edited last in this context.
+ */
+ @Override
+ public String getLastEditedFileName()
+ {
+ Calendar latest = Calendar.getInstance();
+ latest.setTimeInMillis(0);
+
+ LogoFile result = null;
+ for (LogoFile file : context.getFilesTable().values())
+ {
+ Calendar fileDefinedAt = file.getLastSync();
+ if (latest.before(fileDefinedAt))
+ {
+ result = file;
+ latest = fileDefinedAt;
+ }
+ }
+ if (result == null)
+ return null;
+
+ return result.getPlainName();
+ }
+
+ // Change listeners : these event update the gui, they must run on the event dispatcher thread
+
+ @Override
+ public void addFileListener(FileContainerChangeListener listener)
+ {
+ if (listener == null)
+ throw new IllegalArgumentException("listener must not be null.");
+ fileListeners.add(listener);
+ }
+
+ @Override
+ public void removeFileListener(FileContainerChangeListener listener)
+ {
+ fileListeners.remove(listener);
+ }
+
+ private void notifyFileAdded(final String fileName)
+ {
+ if (!context.fireFileEvents())
+ return;
+
+ for (FileContainerChangeListener listener : fileListeners)
+ listener.fileAdded(fileName);
+ }
+
+ private void notifyFileRemoved(final String fileName)
+ {
+ if (!context.fireFileEvents())
+ return;
+
+ for (FileContainerChangeListener listener : fileListeners)
+ listener.fileRemoved(fileName);
+ }
+
+ private void notifyFileRenamed(final String oldName, final String newName)
+ {
+ if (!context.fireFileEvents())
+ return;
+
+ for (FileContainerChangeListener listener : fileListeners)
+ listener.fileRenamed(oldName, newName);
+ }
+
+ private void notifyFileOpened(final String fileName)
+ {
+ if (!context.fireFileEvents())
+ return;
+
+ for (FileContainerChangeListener listener : fileListeners)
+ listener.fileOpened(fileName);
+ }
+
+ private void notifyFileClosed(final String fileName)
+ {
+ if (!context.fireFileEvents())
+ return;
+
+ for (FileContainerChangeListener listener : fileListeners)
+ listener.fileClosed(fileName);
+ }
+
+ private void notifyRightsChanged()
+ {
+ for (FileContainerChangeListener listener : fileListeners)
+ listener.editRightsChanged(context.isFilesListEditAllowed());
+ }
+
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * ERROR COLLECTOR : these events do not update the gui directly, they must not run on the event dispatcher thread
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ @Override
+ public Collection<String> getAllErroneousFiles()
+ {
+ ArrayList<String> erroneousFiles = new ArrayList<String>();
+
+ for(LogoFile file : context.getFilesTable().values())
+ if(file.hasErrors())
+ erroneousFiles.add(file.getPlainName());
+
+ return erroneousFiles;
+ }
+
+ @Override
+ public boolean hasErrors()
+ {
+ for(LogoFile file : context.getFilesTable().values())
+ if(file.hasErrors())
+ return true;
+ return false;
+ }
+
+ @Override
+ public boolean hasErrors(String fileName)
+ {
+ LogoFile file = context.getFilesTable().get(fileName);
+ if (file == null)
+ throw new IllegalStateException("The specified fileName does not exist in this context.");
+
+ return file.hasErrors();
+ }
+
+ @Override
+ public Collection<ProcedureErrorMessage> getAllErrors()
+ {
+ ArrayList<ProcedureErrorMessage> allErrors = new ArrayList<ProcedureErrorMessage>();
+ for (LogoFile file : context.getFilesTable().values())
+ allErrors.addAll(file.getAllErrors());
+ return allErrors;
+ }
+
+ // Error listeners
+
+ private final ArrayList<ErrorListener> errorListeners = new ArrayList<ErrorListener>();
+
+ @Override
+ public void addErrorListener(ErrorListener listener)
+ {
+ errorListeners.add(listener);
+ }
+
+ @Override
+ public void removeErrorListener(ErrorListener listener)
+ {
+ errorListeners.add(listener);
+ }
+
+
+ private void notifyErrorsDetected(String fileName)
+ {
+ if (!context.fireFileEvents())
+ return;
+ for (ErrorListener listener : errorListeners)
+ listener.errorsDetected(fileName);
+ }
+
+ private void notifyErrorsCorrected(String fileName)
+ {
+ if (!context.fireFileEvents())
+ return;
+ for (ErrorListener listener : errorListeners)
+ listener.allErrorsCorrected(fileName);
+ }
+
+
+}
diff --git a/logo/src/xlogo/kernel/userspace/files/RecordFile.java b/logo/src/xlogo/kernel/userspace/files/RecordFile.java new file mode 100644 index 0000000..9220f28 --- /dev/null +++ b/logo/src/xlogo/kernel/userspace/files/RecordFile.java @@ -0,0 +1,234 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program 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 2 of the License, or (at your option)
+ * any later version. This program is 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 program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace.files;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+
+import javax.swing.Timer;
+
+import sun.reflect.generics.reflectiveObjects.NotImplementedException;
+import xlogo.Logo;
+import xlogo.interfaces.MessageBroadcaster;
+import xlogo.messages.async.dialog.DialogMessenger;
+import xlogo.storage.WSManager;
+import xlogo.storage.user.UserConfig;
+import xlogo.utils.Utils;
+
+/**
+ * This is a {@link LogoFile} which is used in contest/record mode.
+ * @author Marko
+ */
+public class RecordFile extends LogoFile implements MessageBroadcaster
+{
+ private static final long serialVersionUID = -9137220313285199168L;
+
+ private Timer timer; // the SWING Timer dispatchers on the EventDispatcher Thread => update GUI ok
+ private Date started;
+ private Date last;
+ private long totalMillis;
+
+ /**
+ * @param fileName
+ */
+ protected RecordFile(String fileName)
+ {
+ super(fileName);
+ }
+
+ public static RecordFile createNewFile(String fileName) throws IOException
+ {
+ RecordFile file = new RecordFile(fileName);
+ file.setupFileSystem();
+ return file;
+ }
+
+
+ /**
+ * @throws NotImplementedException A virtual contest/record mode makes no sense.
+ */
+ public static RecordFile createNewVirtualFile(UserConfig userConfig, String fileName)
+ {
+ throw new NotImplementedException();
+ }
+
+ @Override
+ protected void setupFileSystem() throws IOException
+ {
+ File contestFileDir = getUserConfig().getContestFileDir(getPlainName());
+
+ if (!contestFileDir.exists())
+ contestFileDir.mkdirs();
+ }
+
+ @Override
+ public File getFilePath()
+ {
+ return getUserConfig().getContestFilePath(getPlainName());
+ }
+
+ @Override
+ public void store()
+ {
+ long now = Calendar.getInstance().getTime().getTime();
+ recordFile(getTimeStampHeader(totalMillis, started.getTime(), now));
+ //pauseRecord(); // This is already called by Context at open/close.
+ // We actually never store normally, and we don't export these files.
+ }
+
+ /**
+ * Set the timer
+ */
+ public void startRecord()
+ {
+ this.started = Calendar.getInstance().getTime();
+ this.last = Calendar.getInstance().getTime();
+
+ timer = new Timer(1000,
+ new ActionListener()
+ {
+ public void actionPerformed(ActionEvent arg0)
+ {
+ Date now = Calendar.getInstance().getTime();
+ totalMillis += now.getTime() - last.getTime();
+ last = now;
+
+ String time = UserConfig.getMinSec(totalMillis);
+ String fileName = getPlainName();
+
+ for(MessageListener listener : timerEventListeners)
+ listener.messageEvent(fileName, time);
+ }
+ }
+ );
+ timer.setRepeats(true);
+ timer.start();
+ }
+
+ /**
+ * Stop the timer and record recent changes with time stamp in contest directory.
+ * (Make sure the recent changes from the editor are before calling this)
+ */
+ public void pauseRecord()
+ {
+ timer.stop();
+ }
+
+
+ private void recordFile(final String header)
+ {
+ new Thread(new Runnable(){
+
+ @Override
+ public void run()
+ {
+ // Write to file's folder
+ File recordFile = getUserConfig().getRecordFilePath(getPlainName());
+ File recordFolder = recordFile.getParentFile();
+ if (!recordFolder.exists())
+ recordFolder.mkdirs();
+
+ String content = header + getText();
+
+ try
+ {
+ Utils.writeLogoFile(recordFile.toString(), content);
+ }
+ catch (IOException e)
+ {
+ DialogMessenger.getInstance().dispatchMessage(
+ Logo.messages.getString("contest.error.title"),
+ Logo.messages.getString("contest.error.could.not.record.file") + "\n\n " + e.toString());
+ }
+
+ // append to command line too ...
+ PrintWriter out = null;
+ File logoFile = WSManager.getUserConfig().getCommandLineContestFile();
+ try
+ {
+ out = new PrintWriter(new BufferedWriter(new FileWriter(logoFile, true)));
+ out.println("");
+ out.println(getPlainName());
+ out.println(content);
+ out.println("\n");
+ }
+ catch (Exception e)
+ {
+ DialogMessenger.getInstance().dispatchMessage(Logo.messages.getString("contest.error.title"),
+ Logo.messages.getString("contest.could.not.store") + "\n" + e.toString());
+ }
+ finally
+ {
+ if (out != null)
+ out.close();
+ }
+ }
+ }).run();
+
+ }
+
+
+ private String getTimeStampHeader(long totalTime, long lastEditStarted, long lastEditEnded)
+ {
+ String tot = UserConfig.getMinSec(totalTime);
+ String lastStart = UserConfig.getTimeString(lastEditStarted);
+ String now = UserConfig.getTimeString(lastEditEnded);
+
+ return "# Total Time : " + tot + "\n# Edited from : " + lastStart + "\n# Until : " + now + "\n\n";
+ }
+
+ /*
+ * Timer Listeners
+ */
+
+ private final ArrayList<MessageListener> timerEventListeners = new ArrayList<MessageListener>();
+
+ @Override
+ public void addBroadcastListener(MessageListener listener)
+ {
+ if(listener == null)
+ throw new IllegalArgumentException("Listener must not be null.");
+ timerEventListeners.add(listener);
+ listener.messageEvent(getPlainName(), UserConfig.getMinSec(totalMillis));
+ }
+
+ @Override
+ public void removeBroadcastListener(MessageListener listener)
+ {
+ timerEventListeners.remove(listener);
+ }
+
+}
diff --git a/logo/src/xlogo/kernel/userspace/procedures/ExecutablesContainer.java b/logo/src/xlogo/kernel/userspace/procedures/ExecutablesContainer.java new file mode 100644 index 0000000..73c7295 --- /dev/null +++ b/logo/src/xlogo/kernel/userspace/procedures/ExecutablesContainer.java @@ -0,0 +1,59 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program 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 2 of the License, or (at your option)
+ * any later version. This program is 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 program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace.procedures;
+
+import java.io.IOException;
+import java.util.Collection;
+
+public interface ExecutablesContainer
+{
+
+ public Collection<Procedure> getExecutables();
+
+ public Procedure getExecutable(String procedureName);
+
+ public boolean isExecutable(String name);
+
+ /**
+ * The Logo command {@code eraseProcedure} <p>
+ * Delete the specified procedure. <br>
+ * If there exists an ambiguity and multiple procedures with the same name
+ * are defined, remove all of them.
+ *
+ * @param name
+ */
+ public void eraseProcedure(String procedureName);
+
+ /**
+ * The Logo command {@code define}
+ * @param procedure
+ * @throws IOException
+ */
+ public void defineProcedure(Procedure procedure) throws IOException;
+
+}
diff --git a/logo/src/xlogo/kernel/userspace/procedures/ExecutablesProvider.java b/logo/src/xlogo/kernel/userspace/procedures/ExecutablesProvider.java new file mode 100644 index 0000000..6ae20ea --- /dev/null +++ b/logo/src/xlogo/kernel/userspace/procedures/ExecutablesProvider.java @@ -0,0 +1,36 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program 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 2 of the License, or (at your option)
+ * any later version. This program is 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 program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace.procedures;
+
+import xlogo.interfaces.ProcedureMapper;
+import xlogo.interfaces.ErrorDetector.AmbiguityDetector;
+
+public interface ExecutablesProvider extends ExecutablesContainer, ProcedureMapper, AmbiguityDetector
+{
+
+}
diff --git a/logo/src/xlogo/kernel/userspace/procedures/Procedure.java b/logo/src/xlogo/kernel/userspace/procedures/Procedure.java new file mode 100644 index 0000000..c485432 --- /dev/null +++ b/logo/src/xlogo/kernel/userspace/procedures/Procedure.java @@ -0,0 +1,763 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq + * Copyright (C) 2013 Marko Zivkovic + * + * Contact Information: marko88zivkovic at gmail dot com + * + * This program 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 2 of the License, or (at your option) + * any later version. This program is 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 program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic + * during his Bachelor thesis at the computer science department of ETH Z�rich, + * in the year 2013 and/or during future work. + * + * It is a reengineered version of XLogo written by Lo�c Le Coq, published + * under the GPL License at http://xlogo.tuxfamily.org/ + * + * Contents of this file were initially written Lo�c Le Coq. + * The were heavily refactored, changed and extended by Marko Zivkovic + */ + +/** + * Title : XLogo + * Description : XLogo is an interpreter for the Logo + * programming language + * + * @author Loïc Le Coq + */ +package xlogo.kernel.userspace.procedures; + +import java.util.Calendar; +import java.util.Stack; +import java.util.ArrayList; +import java.util.StringTokenizer; +import java.io.*; + +import xlogo.Logo; +import xlogo.kernel.Primitive; +import xlogo.utils.Utils; + +/** + * The Procedure in XLogo4Schools receives pieces of text, where a single procedure is expected to be defined. + * It parses the text and sets its fields accordingly. + * It maintains states, so we know whether a procedure is executable or whether it has errors and what type of error. + * With the new implementation, multiple types of errors can be detected at the same time. + * + * <p> + * Affichable (displayable) is removed. In XLogo4Schools, every procedure is displayable. + * In XLogo, affichable was added to mark procedures that have been received over the network. + * They should only be running while the TCPConnection is open. + * They won't ever get displayed in the editor and they are removed, after the TCP connection is closed. + * In the new implementation, I can treat the received workspace as a virtual file, + * add its procedures to the UserSpace, without adding the file to the file list. + * Hence it cannot be opened while the procedures can still be executed. + * + * @author Marko Zivkovic, Loic + */ +public class Procedure +{ + public static final String specialCharacters = ":+-*/() []=<>&|"; + + /* Marko : TODO the following properties need to be encapsulated completely (Legacy from XLogo) + * I removed those properties that were named _sauve etc. which was used to temporarily redefine procedures, + * and then restore them in case there was an error while parsing. + */ + + // false lorsque c'est une procédure d'un fichier de démarrage + /** + * Whitespace and comments above a procedure in the editor + */ + public String comment; + public int nbparametre; + public String name; + public ArrayList<String> variable = new ArrayList<String>(); + public Stack<String> optVariables = new Stack<String>(); // Marko : why Stack??? [so bad] + public Stack<StringBuffer> optVariablesExp = new Stack<StringBuffer>(); // Marko : why Stack?? [so bad] + public String instruction = ""; + public StringBuffer instr = null; + + // Marko : I added these + private String text = null; + private String ownerName = null; + private Calendar defineTime = null; + + /** + * Create a procedure from a piece of text from the editor. + * The expected structure is as follows. + * <p> + * [leading <b>empty lines</b> and <b>comments</b> => stored into comment] <br> + * <b>to procedureName</b> [<b>variables</b>] [<b>optional variables</b>] <br> + * [<b>body</b>] <br> + * <b>end</b><br> + * + * @param text + * @throws IOException + */ + public Procedure(String text) + { + try + { + defineTime = Calendar.getInstance(); + this.text = text; + StringReader sr = new StringReader(text); + BufferedReader br = new BufferedReader(sr); + String line = parseComments(br); + if (state == State.COMMENT_ONLY) + return; + StringTokenizer st = new StringTokenizer(line); + parseName(st); + if (errors.contains(ProcedureErrorType.MISSING_TO)) + return; + parseVariables(st); + parseBody(br); + + if (state == State.UNINITIALIZED) + setState(State.EXECUTABLE); + } + catch(IOException ignore) { + /* this should not happen, no actual IO */ + } + } + + /** + * Create a procedure with all necessary values.<br> + * If name is not legal, the Procedure state will switch to error, + * otherwise it will be executable + */ + public void redefine(Procedure newDefinition) + { + defineTime = Calendar.getInstance(); + this.name = newDefinition.name; + this.nbparametre = newDefinition.nbparametre; + this.variable = newDefinition.variable; + this.optVariables = newDefinition.optVariables; + this.optVariablesExp = newDefinition.optVariablesExp; + this.text = newDefinition.text; + this.errors = newDefinition.errors; + + ProcedureErrorType e = checkName(name); + if (e != ProcedureErrorType.NO_ERROR) + addError(e); + else + setState(State.EXECUTABLE); + } + + public Calendar getDefineTime() + { + return defineTime; + } + + public String getText() + { + return text; + } + + /* + * PROCEDURE STATE + */ + + private State state = State.UNINITIALIZED; + + private ArrayList<ProcedureErrorType> errors = new ArrayList<ProcedureErrorType>(); + + /** + * @see {@link State} + * @return + */ + public State getState() + { + return state; + } + + private void setState(State state) + { + this.state = state; + } + + /** + * Use this to indicate that this procedure is not executable because there is an ambiguity within its file. + */ + public void makeAmbiguous() + { + this.state = State.AMBIGUOUS_NAME; + if (!errors.contains(ProcedureErrorType.AMBIGUOUS)) + errors.add(ProcedureErrorType.AMBIGUOUS); + } + + /** + * States are: UNINITIALIZED, EXECUTABLE, COMMENT_ONLY, ERROR + * @author Marko + */ + public enum State + { + /** + * No values are set, or no check has been performed. + */ + UNINITIALIZED("procedure.unititialized"), + /** + * The procedure has a correct structure and can be executed. + */ + EXECUTABLE("procedure.executable"), + /** + * The procedure contains only a comment, no procedure definition. It is not executable + */ + COMMENT_ONLY("procedure.not.executable"), + /** + * The procedure structure could no be parsed entirely because it contains errors. + */ + ERROR("procedure.error"), + /** + * There is another procedures with the same name in the same file. + */ + AMBIGUOUS_NAME("procedure.ambiguous"); + + private String description; + + private State(String description) + { + this.description = description; + } + + public String getDescription() + { + return description; + } + + /** + * @return {@link #getDescription()} + */ + public String toString() + { + return getDescription(); + } + } + + /* + * ERROR REPORTING + */ + + public ArrayList<ProcedureErrorType> getErrors() + { + return errors; + } + + private void addError(ProcedureErrorType e) + { + errors.add(e); + state = State.ERROR; + } + + public String getName() + { + return name; + } + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * OLD XLOGO FEATURES ... TODO behavior not always clear + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + + /** + * @author Lo�c Le Coq + */ + public void decoupe() + { + // Only cut procedures which are visible in the editor + if (null == instr) + { + instr = new StringBuffer(); + try + { + String line = ""; + StringReader sr = new StringReader(instruction); + BufferedReader bfr = new BufferedReader(sr); + int lineNumber = 0; + // Append the number of the line + instr.append("\\l"); + instr.append(lineNumber); + instr.append(" "); + while (bfr.ready()) + { + lineNumber++; + // read the line + try + { + line = bfr.readLine().trim(); + } + catch (NullPointerException e1) + { + break; + } + // delete comments + line = deleteComments(line); + line = Utils.decoupe(line).toString().trim(); + instr.append(line); + if (!line.equals("")) + { + instr.append(" "); + // Append the number of the line + instr.append("\\l"); + instr.append(lineNumber); + instr.append(" "); + } + } + } + catch (IOException e) + {} + // System.out.println("****************************"+name+"\n"+instr+"\n\n"); + } + } + + /** + * @author Lo�c Le Coq + */ + private String deleteComments(String line) + { + int index = line.indexOf("#"); + while (index != -1) + { + if (index == 0) + { + return ""; + } + else if (line.charAt(index - 1) != '\\') + { + return line.substring(0, index); + } + else + index = line.indexOf("#", index + 1); + } + return line; + } + + /** + * @author Lo�c Le Coq + */ + public String toString() + { + // return("nom "+name+" nombre paramètres "+nbparametre+" identifiant "+id+"\n"+variable.toString()+"\n"+instr+"\ninstrction_sauve"+instruction_sauve+"\ninstr_sauve\n"+instr_sauve); + StringBuffer sb = new StringBuffer(); + + sb.append(comment); + sb.append(Logo.messages.getString("pour") + " " + name); + for (int j = 0; j < nbparametre; j++) + { + sb.append(" :"); + sb.append(variable.get(j)); + } + for (int j = 0; j < optVariables.size(); j++) + { + sb.append(" [ :"); + sb.append(optVariables.get(j)); + sb.append(" "); + sb.append(optVariablesExp.get(j)); + sb.append(" ]"); + } + sb.append("\n"); + sb.append(instruction); + sb.append(Logo.messages.getString("fin")); + sb.append("\n"); + // System.out.println("a"+sb+"a"); + return new String(sb); + } + + /** + * @author Lo�c Le Coq + */ + public StringBuffer cutInList() + { + // Only cut procedures which are visible in the editor + StringBuffer sb = new StringBuffer(); + try + { + String line = ""; + StringReader sr = new StringReader(instruction); + BufferedReader bfr = new BufferedReader(sr); + while (bfr.ready()) + { + try + { + line = bfr.readLine().trim(); + } + catch (NullPointerException e1) + { + break; + } + // delete comments + // line=deleteComments(line); + line = Utils.decoupe(line).toString().trim(); + sb.append("[ "); + sb.append(line); + sb.append(" ] "); + } + } + catch (IOException e) + {} + return sb; + } + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * PARSING + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + + /** + * Read and save the comments that appear before the procedure + * @param br + * @return the first line that (probably) contains a procedure definition, + * or null if there are no more procedures + * @throws IOException + * @throws DocumentStructureException + */ + String parseComments(BufferedReader br) throws IOException + { + String line = null; + comment = ""; + while (br.ready()) + { + line = br.readLine(); + if (line == null) + break; + + if (isComment(line)) + { + comment += line + "\n"; + line = null; + } + else if (line.trim().equals("")) + { + comment += "\n"; + line = null; + } + else + break; + } + + if (line == null) + { + setState(State.COMMENT_ONLY); + } + + return line; + } + + /** + * Expects a line that starts with "to procedName" + * @return Error, NO_ERROR if name is ok + * @throws DocumentStructureException + */ + void parseName(StringTokenizer st) + { + String token = st.nextToken(); + + if (!token.toLowerCase().equals(Logo.messages.getString("pour").toLowerCase())) + { + addError(ProcedureErrorType.MISSING_TO); + return; + } + + if (!st.hasMoreTokens()) + { + addError(ProcedureErrorType.MISSING_NAME); + return; + } + + name = st.nextToken().toLowerCase(); + + ProcedureErrorType e = checkName(name); + if (!e.equals(ProcedureErrorType.NO_ERROR)) + addError(e); + } + + void parseVariables(StringTokenizer st) + { + // Then, We read the variables + // :a :b :c :d ..... + String token = null; + while (st.hasMoreTokens()) + { + token = st.nextToken(); + if (token.startsWith(":")) + { + String var = token.substring(1); + ProcedureErrorType e = checkValidVariable(var); + if (e != ProcedureErrorType.NO_ERROR) + { + addError(e); + return; + } + var = var.toLowerCase(); + variable.add(var); + } + else + break; + } + + nbparametre = variable.size(); + + if (token == null || token.startsWith(":")) + return; + + // read all remaining tokens into string buffer + StringBuffer sb = new StringBuffer(); + sb.append(token); + //sb.append(token); + while (st.hasMoreTokens()) + { + sb.append(" "); + sb.append(st.nextToken()); + } + // And finally, optional variables if there are some. + // [:a 100] [:b 20] [:c 234] ........... + + while (sb.length() > 0) + { + if (sb.indexOf("[") != 0) + { + addError(ProcedureErrorType.VAR_EXTRA_CHARS); + return; + } + + sb.deleteCharAt(0); + String[] arg = new String[2]; + extractOptionalVariable(sb, arg); + optVariables.push(arg[0].toLowerCase()); + /* Bug Fixed: list as Optionnal arguments + ** Eg: + ** to a [:var [a b c]] + * end + * when the string is formatted, we check that a white space + * is needed at the end of the argument + */ + + StringBuffer exp = Utils.decoupe(arg[1]); + if (exp.charAt(exp.length() - 1) != ' ') + exp.append(" "); + optVariablesExp.push(exp); + } + } + + /** + * Reads from sb into args the name and the default value of an optional variable [:a value]. <br> + * value can be expression: number, word, list, ... + * @author Lo�c Le Coq + * @author Marko Zivkovic refactored + */ + void extractOptionalVariable(StringBuffer sb, String[] args) + { + + String variable=""; + String expression=""; + int compteur=1; + int id=0; + int id2=0; + boolean espace = false; + for (int i = 0; i < sb.length(); i++) + { + char ch = sb.charAt(i); + if (ch == '[') + compteur++; + else if (ch == ']') + { + if (id == 0) + { + addError(ProcedureErrorType.OPT_VAR_BRACKET); + return; + } + compteur--; + } + else if (ch == ' ') + { + if (!variable.equals("")) + { + if (!espace) + id = i; + espace = true; + } + } + else + { + if (!espace) + variable += ch; + } + if (compteur == 0) + { + id2 = i; + break; + } + } + if (!variable.startsWith(":")) + { + addError(ProcedureErrorType.VAR_COLON_EXPECTED); + return; + } + + variable = variable.substring(1, variable.length()).toLowerCase(); + ProcedureErrorType pet = checkValidVariable(variable); + + if (pet != ProcedureErrorType.NO_ERROR) + { + addError(pet); + return; + } + + + if (compteur != 0) + { + addError(ProcedureErrorType.OPT_VAR_BRACKET); + return; + } + + expression = sb.substring(id + 1, id2).trim(); + + if (expression.equals("")) + { + addError(ProcedureErrorType.VAR_MISSING_EXPRESSION); + return; + } + /* System.out.println(id+" "+id2); + System.out.println("a"+expression+"a"); + System.out.println("a"+variable+"a");*/ + sb.delete(0, id2 + 1); + // delete unnecessary space + while (sb.length() != 0 && sb.charAt(0) == ' ') + sb.deleteCharAt(0); + args[0] = variable; + args[1] = expression; + } + + /** + * Sets as body everything up the 'end' key word. + */ + private void parseBody(BufferedReader br) throws IOException + { + StringBuffer body = new StringBuffer(); + String to = Logo.messages.getString("pour").toLowerCase() + " "; + String end = Logo.messages.getString("fin").toLowerCase(); + instruction = null; + String line; + String lower; + + while (br.ready()) + { + line = br.readLine(); + if (null == line) + break; + lower = line.toLowerCase(); + if (lower.trim().equals(end)) // end of procedure + { + instruction = body.toString(); + if (br.ready() && br.readLine() != null) // Additional characters after end + addError(ProcedureErrorType.CHARS_AFTER_END); + return; // We are done + } + if (lower.startsWith(to)) // new procedure definition before end + { + addError(ProcedureErrorType.TO_BEFORE_END); + return; + } + body.append(line); + body.append("\n"); + } + + // no end was found + addError(ProcedureErrorType.MISSING_END); + + } + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * UTILITY + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + + /** + * @return true if and only if line starts with '#' + */ + static boolean isComment(String line) + { + if (line.trim().startsWith("#")) + return true; + else + return false; + } + + static boolean isNumber(String token) + { + try + { + Double.parseDouble(token); + return true; + } + catch (NumberFormatException e) + { + return false; + } + } + + /** + * This method is essentially changed to fit with the new workspace (=UserSpace & LogoFile).<br> + * On the fly it was rewritten completely, because of its initial programming style and inefficiency. <br> + * (note that it was very long, and now it is only 3 lines) + * + * @param token + * @return Error - NO_ERROR if token is a legal procedure name + * @author Marko Zivkovic + */ + static ProcedureErrorType checkName(String token) + { + if (isNumber(token)) + return ProcedureErrorType.NAME_IS_NUMBER; + ProcedureErrorType e = checkSpecialCharacter(token); + if (e != ProcedureErrorType.NO_ERROR) + return e; + + if(Primitive.primitives.containsKey(token)) + return ProcedureErrorType.NAME_IS_KEY_WORD; + + return ProcedureErrorType.NO_ERROR; + } + + /** + * @param token + * @return null if a valid variable name, error message otherwise + */ + static ProcedureErrorType checkValidVariable(String token) + { + if (token.length() == 0) + return ProcedureErrorType.VAR_WHITESPACE_AFTER_COLON; + + if (isNumber(token)) + return ProcedureErrorType.VAR_IS_NUMBER; + + return checkSpecialCharacter(token); + } + + static ProcedureErrorType checkSpecialCharacter(String var) + { + StringTokenizer check = new StringTokenizer(var, specialCharacters, true); + + if ((check.countTokens() > 1) || ":+-*/() []=<>&|".indexOf(check.nextToken()) > -1) + return ProcedureErrorType.VAR_HAS_SPECIAL; + return ProcedureErrorType.NO_ERROR; + } + + /* + * OWNER : This is typically a file, but a procedure must not know whether it is owned by a file or something else + */ + + public String getOwnerName() + { + return ownerName; + } + + public void setOwnerName(String ownerName) + { + this.ownerName = ownerName; + } + +} diff --git a/logo/src/xlogo/kernel/userspace/procedures/ProcedureErrorType.java b/logo/src/xlogo/kernel/userspace/procedures/ProcedureErrorType.java new file mode 100644 index 0000000..3029320 --- /dev/null +++ b/logo/src/xlogo/kernel/userspace/procedures/ProcedureErrorType.java @@ -0,0 +1,80 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program 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 2 of the License, or (at your option)
+ * any later version. This program is 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 program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace.procedures;
+
+import xlogo.messages.MessageKeys;
+
+public enum ProcedureErrorType
+{
+ NO_ERROR(MessageKeys.LOGO_ERROR_NO_ERROR),
+ MISSING_TO(MessageKeys.LOGO_ERROR_MISSING_TO),
+ MISSING_NAME(MessageKeys.LOGO_ERROR_MISSING_NAME),
+ NAME_IS_NUMBER(MessageKeys.LOGO_ERROR_NAME_IS_NUMBER), //Logo.messages.getString("erreur_nom_nombre_procedure")
+ /*
+ * Logo.messages.getString("caractere_special1") + "\n" + Logo.messages.getString("caractere_special2")
+ * + "\n" + Logo.messages.getString("caractere_special3") + " " + token
+ */
+ NAME_HAS_SPECIAL_CHAR(MessageKeys.LOGO_ERROR_NAME_SPECIAL),
+ VAR_WHITESPACE_AFTER_COLON(MessageKeys.LOGO_ERROR_VAR_WHITE_AFTER_COLON),
+ /*
+ * Logo.messages.getString("caractere_special_variable") + "\n"
+ + Logo.messages.getString("caractere_special2") + "\n"
+ + Logo.messages.getString("caractere_special3") + " :" + var
+ */
+ VAR_HAS_SPECIAL(MessageKeys.LOGO_ERROR_VAR_SPECIAL),
+ VAR_IS_NUMBER(MessageKeys.LOGO_ERROR_VAR_IS_NUMBER),
+ VAR_COLON_EXPECTED(MessageKeys.LOGO_ERROR_VAR_MISSING_COLON),
+ OPT_VAR_BRACKET(MessageKeys.LOGO_ERROR_OPT_VAR_BRACKET),
+ VAR_EXTRA_CHARS(MessageKeys.LOGO_ERROR_VAR_EXTRA_CHARS),
+ VAR_MISSING_EXPRESSION(MessageKeys.LOGO_ERROR_MISSING_EXPR),
+ CHARS_AFTER_END(MessageKeys.LOGO_ERROR_MORE_CHARS_AFTER_END),
+ MISSING_END(MessageKeys.LOGO_ERROR_MISSING_END),
+ TO_BEFORE_END(MessageKeys.LOGO_ERROR_TO_BEFORE_END),
+ NAME_IS_KEY_WORD(MessageKeys.LOGO_ERROR_NAME_IS_KEY_WORD),
+ AMBIGUOUS(MessageKeys.LOGO_ERROR_AMBIGUOUS);
+
+ private String description;
+
+ private ProcedureErrorType(String description)
+ {
+ this.description = description;
+ }
+
+ public String getDescription()
+ {
+ return description;
+ }
+
+ /**
+ * @return {@link #getDescription()}
+ */
+ public String toString()
+ {
+ return getDescription();
+ }
+}
\ No newline at end of file diff --git a/logo/src/xlogo/kernel/userspace/procedures/ProceduresManager.java b/logo/src/xlogo/kernel/userspace/procedures/ProceduresManager.java new file mode 100644 index 0000000..bcaea16 --- /dev/null +++ b/logo/src/xlogo/kernel/userspace/procedures/ProceduresManager.java @@ -0,0 +1,557 @@ +/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program 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 2 of the License, or (at your option)
+ * any later version. This program is 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 program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were entirely written by Marko Zivkovic
+ */
+
+package xlogo.kernel.userspace.procedures;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import xlogo.kernel.userspace.ProcedureErrorMessage;
+import xlogo.kernel.userspace.context.ContextSwitcher;
+import xlogo.kernel.userspace.context.LogoContext;
+import xlogo.kernel.userspace.context.ContextSwitcher.ContextSwitchListener;
+import xlogo.kernel.userspace.files.LogoFile;
+import xlogo.kernel.userspace.procedures.Procedure.State;
+
+/**
+ * This class maintains the procedure table of all files in a context, and it reports ambiguity among them.
+ * @author Marko Zivkovic
+ */
+public class ProceduresManager implements ExecutablesProvider
+{
+ private LogoContext context;
+
+ private final ArrayList<AmbiguityListener> ambiguityListeners = new ArrayList<AmbiguityListener>();
+
+ private ProcedureMapListener procedureMapListener;
+
+ public ProceduresManager(ContextSwitcher contextProvider)
+ {
+ initProcedureMapListener();
+ installContextSwitchListener(contextProvider);
+ setContext(contextProvider.getContext());
+ }
+
+ private void setContext(LogoContext newContext)
+ {
+ LogoContext old = this.context;
+ this.context = newContext;
+
+ if (old != null)
+ old.removeProcedureMapListener(procedureMapListener);
+
+ mapProcedures(newContext);
+
+ /* Record Mode => new files => events
+ // Network Mode => only virtual files, invisible => no events
+ // that was achieved with the boolean "affichable" (displayable) in XLogo
+ if (!newContext.fireProcedureEvents())
+ {
+
+ return;
+ }
+ */
+ if (old != null && old.fireProcedureEvents() && newContext.fireProcedureEvents()) // TODO changed
+ for(LogoFile file : old.getFilesTable().values())
+ notifyDeleted(file.getPlainName(), file.getAllProcedureNames());
+
+ // Note : context will immediately fire event to notify what his contents are.
+ // Then these events are forwarded here, in procedureMapListener.
+ newContext.addProcedureMapListener(procedureMapListener);
+ }
+
+ private void mapProcedures(LogoContext context)
+ {
+ context.getProcedureTable().clear();
+ for (LogoFile file : context.getFilesTable().values())
+ for (Procedure procedure : file.getExecutables())
+ addProcedure(procedure);
+ }
+
+ private void installContextSwitchListener(ContextSwitcher provider)
+ {
+ provider.addContextSwitchListener(new ContextSwitchListener(){
+
+ @Override
+ public void contextSwitched(LogoContext newContext)
+ {
+ setContext(newContext);
+ }
+ });
+ }
+
+ private void initProcedureMapListener()
+ {
+ // This one listens for changes in files.
+ procedureMapListener = new ProcedureMapListener()
+ {
+ /**
+ * This event is received when a document is assigned a new text and it contains no errors,
+ * or when this ProcedureManager is assigned a new context that already contains files.
+ */
+ @Override
+ public void defined(String fileName, Collection<String> procedures)
+ {
+ for (String procedureName : procedures)
+ addProcedure(fileName, procedureName);
+
+ if (procedures.size() > 0)
+ notifyDefined(fileName, procedures);
+
+ for (String procedureName : procedures)
+ {
+ Map<String, Procedure> fileNameToProcedure = context.getProcedureTable().get(procedureName);
+ if (fileNameToProcedure.size() > 1)
+ {
+ notifyAmbiguityDetected(procedureName, context.getProcedureTable().get(procedureName));
+ }
+ }
+
+ }
+
+ /**
+ * This event is received when the Logo command "define" (re-)defined a command in a file (which already exists!)
+ */
+ @Override
+ public void defined(String fileName, String procedureName)
+ {
+ addProcedure(fileName, procedureName);
+
+ notifyDefined(fileName, procedureName);
+
+ // Check ambiguity
+ Map<String, Procedure> fileNameToProcedure = context.getProcedureTable().get(procedureName);
+ if (fileNameToProcedure.size() > 1)
+ {
+ notifyAmbiguityDetected(procedureName, fileNameToProcedure);
+ return;
+ }
+ }
+
+ /**
+ * This event is received when a file is deleted or when it is assigned a new text in which old procedures are missing.
+ */
+ @Override
+ public void undefined(String fileName, Collection<String> procedures)
+ {
+ for (String procedure : procedures)
+ undefined(fileName, procedure);
+ }
+
+ /**
+ * This event is received when the Logo command "eraseProcedure" removes a single procedure from a document
+ * Depending on how many procedures with the same name are left across files after this procedure is removed,
+ * this will fire ExecutableChanged events or AmbiguityResolved events.
+ */
+ @Override
+ public void undefined(String fileName, String procedureName)
+ {
+ Map<String,HashMap<String, Procedure>> procedureTable = context.getProcedureTable();
+
+ Map<String, Procedure> fileToProc = procedureTable.get(procedureName);
+
+ if (fileToProc == null)
+ return;
+
+ // remove from fileToProc entry
+ fileToProc.remove(fileName);
+
+ notifyDeleted(fileName, procedureName);
+
+ if (fileToProc.size() == 0)
+ procedureTable.remove(procedureName);
+ else
+ notifyAmbiguityResolved(procedureName, fileName);
+
+ if (fileToProc.size() == 1)
+ {
+ String winnerFile = null;
+ for (String wf : fileToProc.keySet())
+ winnerFile = wf; // size == 1 => only 1 iteration
+
+ notifyAmbiguityResolved(procedureName, winnerFile);
+ }
+ }
+
+ @Override
+ public void ownerRenamed(String oldName, String newName)
+ {
+ // Note : very important that this event is catched after filemanager has renamed files
+ // very critical. but implementation guarantees this order.
+ LogoFile file = context.getFilesTable().get(newName);
+ // Rename keys in procedureTable
+ Map<String, Procedure> fileToProc;
+ for (Procedure proc : file.getExecutables())
+ {
+ fileToProc = context.getProcedureTable().get(proc.getName());
+ fileToProc.remove(oldName);
+ fileToProc.put(newName, proc);
+ }
+ }
+ };
+ }
+
+ /**
+ * @see #addProcedure(Procedure)
+ */
+ protected void addProcedure(String fileName, String procedureName) throws IllegalArgumentException
+ {
+ Procedure procedure = context.getFilesTable().get(fileName).getExecutable(procedureName.toLowerCase());
+ addProcedure(procedure);
+ }
+
+ /**
+ * Adds the procedure to the procedureTable without firing events.
+ * Use {@link #defineProcedure(Procedure)}} if you want to notify listeners
+ * @param procedure
+ * @throws IllegalArgumentException
+ */
+ protected void addProcedure(Procedure procedure) throws IllegalArgumentException
+ {
+ String procedureName = procedure.getName();
+
+ if (procedure.getState() != State.EXECUTABLE && procedure.getState() != State.AMBIGUOUS_NAME)
+ throw new IllegalArgumentException("Only executable procedures should be added to the context.");
+
+ HashMap<String, HashMap<String, Procedure>> procedureTable = context.getProcedureTable();
+ HashMap<String, Procedure> fileToProc = procedureTable.get(procedureName);
+
+ if (fileToProc == null) // The procedure name is not yet defined in the context.
+ {
+ // Must create fileToProc entry in procedureTable
+ fileToProc = new HashMap<String, Procedure>();
+ procedureTable.put(procedureName, fileToProc);
+ }
+
+ fileToProc.put(procedure.getOwnerName(), procedure);
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * EXECUTABLES CONTAINER
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ /**
+ * @param {@link Procedure#getOwnerName()} is supposed to be already mapped in the filesTable, and the procedure should be executable
+ * @throws IllegalArgumentException if the procedure's owner name is not mapped;
+ * @see {@link LogoFile#defineProcedure(Procedure)}
+ */
+ @Override
+ public void defineProcedure(Procedure procedure)
+ {
+ LogoFile file = context.getFilesTable().get(procedure.getOwnerName());
+ if (file == null)
+ throw new IllegalArgumentException("The file name \""
+ + procedure.getOwnerName()
+ + "\" specified in procedure is not mapped to a file.");
+
+ //addProcedure(procedure);
+ file.defineProcedure(procedure); // this will cause the necessary event cascade
+ }
+
+ /**
+ * Erase
+ */
+ @Override
+ public void eraseProcedure(String procedureName)
+ {
+ String lower = procedureName.toLowerCase();
+ while(context.getProcedureTable().containsKey(lower))
+ {
+ // contains key => there must be one
+ Procedure nextVictim = null;
+ for (Procedure proc : context.getProcedureTable().get(lower).values())
+ {
+ // iterate only once : otherwise concurrent modification exception
+ nextVictim = proc;
+ break;
+ }
+ context.getFilesTable().get(nextVictim.getOwnerName()).eraseProcedure(nextVictim.getName());
+ }
+ }
+
+ /**
+ * Executable procedures of all files. Ambiguous procedures are not included. <br>
+ * This list should not be used to do computations on it, because it must be created every time.
+ * Its purpose is to feed GUI lists
+ */
+ @Override
+ public ArrayList<Procedure> getExecutables()
+ {
+ ArrayList<Procedure> executables = new ArrayList<Procedure>();
+ for(Map<String, Procedure> fileToProc : context.getProcedureTable().values())
+ if (fileToProc.size() == 1) // only one exists
+ for(Procedure proc : fileToProc.values())
+ executables.add(proc); // only one is added
+ return executables;
+ }
+
+ /**
+ * @return The specified procedure if and only if there is exactly one
+ * procedure definition for this name in the context, otherwise null.
+ */
+ @Override
+ public Procedure getExecutable(String procedureName)
+ {
+ Map<String, Procedure> fileToProc = context.getProcedureTable().get(procedureName.toLowerCase());
+
+ if (fileToProc == null || fileToProc.size() > 1)
+ return null;
+ // There is exactly 1 procedure
+ for (Procedure proc : fileToProc.values())
+ return proc;
+ // Code won't reach here
+ return null;
+ }
+
+ /**
+ * @return true if and only if there exists exactly one procedure definition for the specified name in this context
+ */
+ @Override
+ public boolean isExecutable(String procedureName)
+ {
+ Map<String, Procedure> fileToProc = context.getProcedureTable().get(procedureName.toLowerCase());
+ return (fileToProc != null && fileToProc.size() == 1);
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * PROCEDURE MAPPER
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ /**
+ * Get all executable procedure names
+ * @see #getExecutables()
+ */
+ @Override
+ public Collection<String> getAllProcedureNames()
+ {
+ ArrayList<String> executables = new ArrayList<String>();
+ for(Map<String, Procedure> fileToProc : context.getProcedureTable().values())
+ if (fileToProc.size() == 1) // only one exists
+ for(Procedure proc : fileToProc.values())
+ executables.add(proc.getName()); // only one is added
+ return executables;
+ }
+
+ /**
+ * Get all procedures of that file, if its procedures are not conflicting with other files' procedures.
+ */
+ @Override
+ public Collection<String> getAllProcedureNames(String fileName)
+ {
+ if (!hasAmbiguousProcedures(fileName))
+ return context.getFilesTable().get(fileName).getAllProcedureNames();
+ return null;
+ }
+
+ /**
+ * @return null if procedure does not exist or if it is ambiguous
+ */
+ @Override
+ public String getProcedureOwner(String procedureName)
+ {
+ Procedure procedure = getExecutable(procedureName.toLowerCase());
+ return procedure == null ? null : procedure.getOwnerName();
+ }
+
+ // Procedure Map Listeners : update gui => run on event dispatcher thread
+
+ private final ArrayList<ProcedureMapListener> procedureMapListeners = new ArrayList<ProcedureMapListener>();
+
+ @Override
+ public void addProcedureMapListener(ProcedureMapListener listener)
+ {
+ procedureMapListeners.add(listener);
+ }
+
+ @Override
+ public void removeProcedureMapListener(ProcedureMapListener listener)
+ {
+ procedureMapListeners.remove(listener);
+ }
+
+ protected void notifyDefined(final String fileName, final Collection<String> procedures)
+ {
+ if (!context.fireProcedureEvents())
+ return;
+ if (procedures.size() == 0)
+ return;
+ for (ProcedureMapListener listener : procedureMapListeners)
+ listener.defined(fileName, procedures);
+ }
+
+ protected void notifyDefined(final String fileName, final String procedure)
+ {
+ if (!context.fireProcedureEvents())
+ return;
+ for (ProcedureMapListener listener : procedureMapListeners)
+ listener.defined(fileName, procedure);
+
+ }
+
+ protected void notifyDeleted(final String fileName, final Collection<String> procedures)
+ {
+ if (!context.fireProcedureEvents())
+ return;
+ if (procedures.size() == 0)
+ return;
+
+ for (ProcedureMapListener listener : procedureMapListeners)
+ listener.undefined(fileName, procedures);
+ }
+
+ protected void notifyDeleted(final String fileName, final String procedure)
+ {
+ if (!context.fireProcedureEvents())
+ return;
+ for (ProcedureMapListener listener : procedureMapListeners)
+ listener.undefined(fileName, procedure);
+ }
+
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * AMBIGUITY DETECTOR
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ */
+
+ /**
+ * @return Whether there exists a procedure name that is defined in more
+ * than one file in the UserSpace
+ */
+ @Override
+ public boolean hasErrors()
+ {
+ for (Entry<String, ? extends Map<String, Procedure>> entry : context.getProcedureTable().entrySet())
+ {
+ if (entry.getValue().size() > 1)
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns all ambiguity problems
+ */
+ @Override
+ public Collection<ProcedureErrorMessage> getAllErrors()
+ {
+ ArrayList<ProcedureErrorMessage> ambiguities = new ArrayList<ProcedureErrorMessage>();
+
+ for (Entry<String, HashMap<String, Procedure>> entry : context.getProcedureTable().entrySet())
+ {
+ HashMap<String, Procedure> fileToProc = entry.getValue();
+
+ if (fileToProc.size() < 2)
+ continue;
+
+ ambiguities.add(
+ new ProcedureErrorMessage(
+ ProcedureErrorType.AMBIGUOUS,
+ entry.getKey(),
+ fileToProc.keySet()
+ )
+ );
+ }
+
+ return ambiguities;
+ }
+
+ @Override
+ public Collection<String> getAllConflictingFiles()
+ {
+ ArrayList<String> conflictingFiles = new ArrayList<String>();
+
+ for (Entry<String, HashMap<String, Procedure>> entry : context.getProcedureTable().entrySet())
+ {
+ HashMap<String, Procedure> fileToProc = entry.getValue();
+
+ if (fileToProc.size() < 2)
+ continue;
+
+ conflictingFiles.addAll(fileToProc.keySet());
+ }
+
+ return conflictingFiles;
+ }
+
+ @Override
+ public boolean hasAmbiguousProcedures(String fileName)
+ {
+ for (Procedure proc : context.getFilesTable().get(fileName).getExecutables())
+ {
+ if(isProcedureAmbiguous(proc.getName()))
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isProcedureAmbiguous(String name)
+ {
+ Map<String, Procedure> ambigs = context.getProcedureTable().get(name.toLowerCase());
+ if (ambigs == null)
+ return false;
+ return ambigs.size() > 1;
+ }
+
+
+ @Override
+ public void addAmbiguityListener(AmbiguityListener listener)
+ {
+ ambiguityListeners.add(listener);
+ }
+
+ @Override
+ public void removeAmbiguityListener(AmbiguityListener listener)
+ {
+ ambiguityListeners.remove(listener);
+ }
+
+ private void notifyAmbiguityDetected(String procedureName, Map<String, Procedure> fileToProcedure)
+ {
+ if(!context.fireProcedureEvents())
+ return;
+ for (AmbiguityListener listener : ambiguityListeners)
+ listener.ambiguityDetected(procedureName, fileToProcedure);
+ }
+
+ private void notifyAmbiguityResolved(String procedureName, String winnerFile)
+ {
+ if(!context.fireProcedureEvents())
+ return;
+ for (AmbiguityListener listener : ambiguityListeners)
+ listener.ambiguityResolved(procedureName, winnerFile);
+ }
+
+
+
+
+}
|