import java.util.ArrayList;

public class Geom {

    public static void apply(Operation o, Element e) {
	o.operate(e);
    }

    public static void visit(Visitor vr, Visitable vb) {
	vb.accept(vr);
    }
    
    public static void iterate(Operation o, Iterable c) {
    	Iterator it = c.getIterator();
    	while (it.hasNext()) {
    	    o.operate(it.next());
    	}
    }

    public static void convertToSvg(Element e) {
	System.out.println("<svg version=\"1.1\" baseProfile=\"full\" xmlns=\"http://www.w3.org/2000/svg\">");
	e.accept(new ConvertToSvg());
	System.out.println("</svg>");
    }

    public static void main(String[] args) {
	Line l1 = new Line(0, 0, 50, 50, "red");
	Circle c1 = new Circle(25, 25, 10, "blue");
	Container ct = new Container("green");
	ct.addElement(l1);
	ct.addElement(c1);
	convertToSvg(ct);
    }
}

/* Interfaces */

interface Visitable {
    public void accept(Visitor v);
}

interface Visitor {
    public void visitLine(Line l);
    public void visitCircle(Circle c);
    public void visitContainerIn(Container ct);
    public void visitContainerOut(Container ct);
}

/* Elements */

abstract class Element {
    private static int counter = 0;
    protected final int id;
    protected String colour;
    
    public Element(String c) {
	this.id = counter++;
	this.colour = c;
    }
    
    public void changeColour(String c) {
        this.colour = c;
    }

    abstract public String toStringIndent(String offset);
    abstract public void zoom(int z);
    abstract public String toString();
    abstract public String toSVG();    
}

class Line extends Element {
    protected int x1, y1;
    protected int x2, y2;
    
    public Line(int x1, int y1, int x2, int y2, String c) {
	super(c);
	this.x1 = x1;
        this.y1 = y1;
	this.x2 = x2;
        this.y2 = y2;
    }

    public String toString() {
    	return "Line " + this.id + " " + this.colour;
    }
    
    public String toStringIndent(String offset) {
    	return offset + this.toString() + "\n";
    }
    
    public void zoom(int z) {
    	this.x1 *= z;
        this.y1 *= z;
	this.x2 *= z;
        this.y2 *= z;
    }

    public String toSVG() {
        return
            "<line x1=\"" + this.x1
            + "\" x2=\"" + this.x2
            + "\" y1=\"" + this.y1
            + "\" y2=\"" + this.y2
            + "\" stroke=\"" + this.colour
            + "\"/>";
    }
}

class Circle extends Element {
    protected int x, y;
    protected int r;
    
    public Circle(int x, int y, int r, String c) {
	super(c);
	this.x = x;
        this.y = y;
	this.r = r;
    }

    public String toString() {
    	return "Circle " + this.id + " " + this.colour;
    }
    
    public String toStringIndent(String offset) {
    	return offset + this.toString() + "\n";
    }

    public void zoom(int z) {
    	this.x *= z;
        this.y *= z;
	this.r *= z;
    }

    public String toSVG() {
	return
            "<circle cx=\"" + this.x
            + "\" cy=\"" + this.y
            + "\" r=\"" + this.r
            + "\" stroke=\"" + this.colour
            + "\"/>";
    }

}

class Container extends Element {
    private ArrayList<Element> elements;
    
    public Container(String c) {
	super(c);
	this.elements = new ArrayList<Element>();
    }
    
    public void addElement(Element e) {
	elements.add(e);
    }
    
    public String toString() {
        String res = "Container " + ct.id + " " + ct.colour + " [";
        for (Element elt: this.elements) {
            res += elt.toString() + ", ";
        }
        res += "]";
        return res;
    }

    public String toStringIndent(String offset) {
        String res = offset + "Container " + ct.id + " " + ct.colour + "\n";
        for (Element elt: this.elements) {
            res += elt.toStringIndent(offset + "  ");
        }
        return res;
    }

    public void zoom(int z) {}

    public String toSVG() {
        String res = "<g fill=\"" + ct.colour + "\">";
        for (Element elt: this.elements) {
            res += elt.toSVG();
        }
        res += "</g>";
        return res;
    }
    
}

/* Operations */

abstract class Operation implements Visitor {
    abstract public void operate(Element e);
}

class Print extends Operation {   
    public void operate(Element e) {
	System.out.println("Element " + e.id + " " + e.colour);
    }
    
    public void operateLine(Line l) {
    	System.out.println("Line " + l.id + " " + l.colour);
    }    
    public void operateCircle(Circle c) {
    	System.out.println("Circle " + c.id + " " + c.colour);
    }

    public void visitLine(Line l) {
    	System.out.println("Line " + l.id + " " + l.colour);
    }    
    public void visitCircle(Circle c) {
    	System.out.println("Circle " + c.id + " " + c.colour);
    }
    public void visitContainerIn(Container ct) {
    	System.out.println("Container " + ct.id + " " + ct.colour);
    }
    public void visitContainerOut(Container ct) {}
}

class ChangeColour extends Operation {
    private String colour;

    public ChangeColour(String c) {
	colour = c;
    }
    
    public void operate(Element e) {
	e.colour = colour;
    }

    public void visitLine(Line l) { operate(l); }
    public void visitCircle(Circle c) { operate(c); }
    public void visitContainerIn(Container ct) { operate(ct); }
    public void visitContainerOut(Container ct) {}
}
    

class Translate extends Operation {
    private int dx, dy;    

    public Translate(int dx, int dy) {
	dx = dx; dy = dy;
    }

    public void operate(Element e) {
	if (e instanceof Line) {
	    Line l = (Line)e;
	    l.x1 += dx; l.y1 += dy;
	    l.x2 += dx; l.y2 += dy;
	} else {
	    Circle c = (Circle)e;
	    c.x += dx; c.y += dy;	    
	}
    }

    public void visitLine(Line l) {
	l.x1 += dx; l.y1 += dy;
	l.x2 += dx; l.y2 += dy;
    }
    public void visitCircle(Circle c) {
	c.x += dx; c.y += dy;	    
    }
    public void visitContainerIn(Container ct) {}
    public void visitContainerOut(Container ct) {}
}

class Zoom extends Operation {
    private int z;

    public Zoom(int z) {
	z = z;
    }
    
    public void operate(Element e) {
	e.zoom(z);
    }

    public void visitLine(Line l) {
    	l.x1 *= z; l.y1 *= z;
	l.x2 *= z; l.y2 *= z;
    }
    public void visitCircle(Circle c) {
    	c.x *= z; c.y *= z;
	c.r *= z;
    }
    public void visitContainerIn(Container ct) {}
    public void visitContainerOut(Container ct) {}
}


class ConvertToSvg extends Operation {
    public void operate(Element e) {}
    
    public void visitLine(Line l) {
	System.out.println("<line x1=\"" + l.x1 + "\" x2=\"" + l.x2 +
			   "\" y1=\"" + l.y1 + "\" y2=\"" + l.y2 +
			   "\" stroke=\"" + l.colour + "\"/>");
    }
    public void visitCircle(Circle c) {
	System.out.println("<circle cx=\"" + c.x + "\" cy=\"" + c.y +
			   "\" r=\"" + c.r +
			   "\" stroke=\"" + c.colour + "\"/>");
    }
    public void visitContainerIn(Container ct) {
	System.out.println("<g fill=\"" + ct.colour + "\">");
    }
    public void visitContainerOut(Container ct) {
	System.out.println("</g>");
    }
}