# XML write Problem zweiter Ansatz



## MarioK (24. Jun 2011)

Hallo Gemeinschaft,
nachdem ich hier http://www.java-forum.org/xml-co/120551-xml-write-problem.html schon ein guten Ansatz für write gefunden hatte, möchte sich dieses leider nicht in meiner neuen Aufgabe umsetzen lassen.

ich möchte gern, wenn der Benutzer mit "insert" einige Kreise auf der Zeichenfläche gezeichnet hat und er anschliessend auf "save" wechselt, dass diese Kreise als .xml Datei gespeichert werden.
So sollte es aussehen, sprich die X und Y Koordinaten des Kreises sollten abgelegt werden, um sie dann später mit "open" wieder neu zu generieren:
[XML]<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Knoten>
    <koordinaten>
        <xKoord>111</xKoord>
        <yKoord>222</yKoord>
    </koordinaten>
    <koordinaten>
        <xKoord>333</xKoord>
        <yKoord>444</yKoord>
    </koordinaten>
</Knoten>[/XML]

Mit den folgenden Code (siehe Zeile 318 - 339)

```
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.*;

import java.util.ArrayList;
import java.util.Iterator;
 
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.filechooser.FileFilter;
  
/**
 * Dieses Programm stellt dem Benutzer eine grafische Benutzeroberfläche zur
 * Verfügung, womit dieser auf einer Zeichenfläche Punkte (Kreise) zeichnen,
 * löschen und verschieben kann. Dabei kann der Benutzer die Interaktionen
 * aus ein Menu wählen.
 * 
 * @author Mario Krüger
 * Matrikelnummer 768573
 * Version: 14.06.2011
 *
 */
public class MyGraphEditor extends JFrame{

	private static final long serialVersionUID = 2819070241694183052L;
	
	// Variablen initialisieren
    private final PunktVerarbeiten zeichnenFenster = new PunktVerarbeiten();
    private boolean insertPoint 	= false;
    private boolean deletePoint 	= false;
    private boolean movePoint 		= false;
    @SuppressWarnings("unused")
	private boolean nonePoint 		= false;
    @SuppressWarnings("unused")
	private boolean openP 			= false;
    @SuppressWarnings("unused")
	private boolean newP 			= false;
    @SuppressWarnings("unused")
	private boolean saveP 			= false;
    private boolean correctPoint 	= false;
    
    private ArrayList<Point> kreise;
    Point ls1;
    Point ls2;
    Point ls3;
	
    final int radius = 10;
    private final JMenuBar mb;
    private final JMenu graph, vertex; 
    private final JMenuItem open, neu, save, insert, move, delete, none;
    
    private final JFileChooser jfc = new JFileChooser();
    
    JLabel label;
    JTextField text;

    
    // Kontruktor erstellen
    public MyGraphEditor(){
    	
        final JFrame frame = new JFrame("MyGraphEditor"); 
        
        // Menu erstellen
        mb = new JMenuBar();
        
        // Menuitem Graph erstellen
        graph = new JMenu("Graph");
 
        open = new JMenuItem("open");
        open.addActionListener(new MenuAktion());
        graph.add(open);
        graph.addSeparator();
 
        neu = new JMenuItem("new");
        neu.addActionListener(new MenuAktion());
        graph.add(neu);
        graph.addSeparator();
        
        save = new JMenuItem("save");
        save.addActionListener(new MenuAktion());
        graph.add(save);
        graph.addSeparator();
        
        // Menuitem Vertex erstellen
        vertex = new JMenu("Vertex");
 
        insert = new JMenuItem("insert");
        insert.addActionListener(new MenuAktion());
        vertex.add(insert);
        vertex.addSeparator();
 
        delete = new JMenuItem("delete");
        delete.addActionListener(new MenuAktion());
        vertex.add(delete);
        vertex.addSeparator();
        
        move = new JMenuItem("move");
        move.addActionListener(new MenuAktion());
        vertex.add(move);
        vertex.addSeparator();
        
        none = new JMenuItem("none");
        none.addActionListener(new MenuAktion());
        vertex.add(none);

        mb.add(graph);
        mb.add(vertex);
        
        // Panel + Label + Textfeld erstellen
        final JPanel panel = new JPanel(new FlowLayout());
        text = new JTextField("none", 20);
        text.setEditable(false);
        label = new JLabel("Selected Action");       
        panel.add(label);
        panel.add(text);
        
        // Frame zusammenstellen
        frame.setJMenuBar(mb);
        frame.setLayout(new BorderLayout());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        zeichnenFenster.setPreferredSize(new Dimension(800,500));
        frame.add(zeichnenFenster,BorderLayout.CENTER);
        frame.add(panel,BorderLayout.SOUTH);
        frame.pack();
        frame.setVisible(true);
        frame.setLocationRelativeTo(null);
        
        //mit dieser Methode wird der JFileChooser erweitert
        //um einen Filter für .xml Dateien
        jfc.addChoosableFileFilter(new FileFilter(){
			
			public boolean accept(File f) {
				if (f.isDirectory()) return true;
				return f.getName().toLowerCase().endsWith(".xml");
			}

			public String getDescription() {
				return ".xml";
			}
			
		});
        
    }
	
    /**
     * Diese Klasse zeichnet die Punkte (Kreise) und legt die 
     * Mausinteraktionen fest.
     *
     */
    class PunktVerarbeiten extends JComponent implements MouseListener{
	
		private static final long serialVersionUID = -7292895371227480384L;
		
		// Kontruktor erstellen.
		public PunktVerarbeiten() {
			
    		kreise = new ArrayList<Point>();
    		addMouseListener(this) ;
    		
    	}
    	
		// Festlegen der Zeichenflächenfarbe, der Punktfarbe und zeichnen 
		// der Kreise.
    	@Override
    	protected void paintComponent(final Graphics g) {
    		
    		super.paintComponent(g);
    		final Graphics2D g2D = (Graphics2D) g;
    		g2D.setColor(Color.white);
    		g2D.fillRect(0,0,getWidth(), getHeight());
    		g2D.setColor(Color.black);
    		g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
    				RenderingHints.VALUE_ANTIALIAS_ON);
    		for (final Point p : kreise){
    			g2D.fillOval(p.x-10/2, p.y-10/2, radius, radius);
    		}
    		
    	}
    	
    	// festlegen der Mausinteraktionen
    	public void mouseClicked(final MouseEvent e) {
        	
    		// wenn insert aus dem Menu erwählt wurde, dann wird in der 
    		// Zeichenfläche beim Klick ein Punkt gezeichnet und 
    		// gleichzeitig werden die Koordinaten der ArrayList kreise 
    		// hinzugefügt.
            if (insertPoint == true){
            	ls1 = e.getPoint();
            	kreise.add(ls1);
                repaint();
            }
            
            // wenn delete aus dem Menu erwählt wurde, dann wird beim 
            // Klick auf die Zeichenfläche überprüft, ob der Klick in 
            // der Nähe eines Punktes ist. Wenn der Klick innerhalb 
            // eines Punktes, wird dieser gelöscht, parallel zum 
            // Löschen aus der ArrayList kreise.
            else if (deletePoint){
                ls1 = e.getPoint();
                for(final Iterator<Point> iterator = kreise.iterator(); 
                		iterator.hasNext();){
                	final Point p = iterator.next();
                	ls2 = e.getPoint();
                	if(p.distance(ls2) <= (radius/2)){
                		iterator.remove();
                		repaint();
                	}
                }
            }
                         
        }

		public void mousePressed(final MouseEvent e) {
			
			// wenn move aus dem Menu erwählt wurde, dann wird beim 
			// Maus gedrückt halten, die Koordinate vom dem zu 
			// versetzenden Kreis gespeichert, wenn der Klick
			// innerhalb eines Kreise liegt.
			if(movePoint){
				ls1 = e.getPoint();
                for(final Iterator<Point> iterator = kreise.iterator(); 
                		iterator.hasNext();){
                	final Point p = iterator.next();
                	ls2 = e.getPoint();
                	if(p.distance(ls2) <= (radius/2)){                		
                		ls3 = p;
                		movePoint = true;
                		correctPoint = true;
                	}
                }
			}
			
		}
		
		public void mouseReleased(final MouseEvent e) {
			
			// wenn move aus dem Menu erwählt wurde, dann wird beim 
			// Maus loslassen der Punkt versetzt, wenn MousePressed 
			// positiv abgeschlossen wurde, gleichzeitig wird der 
			// ArrayList kreise die Koordinaten des Punktes 
			// hinzugefügt und die alten Werte des Punktes gelöscht.
			if(movePoint & correctPoint){
				ls1 = e.getPoint();
				kreise.add(ls1);
				kreise.remove(ls3);
				correctPoint = false;
				repaint();			
			}
			
		}

		public void mouseEntered(final MouseEvent e) {}	
		public void mouseExited(final MouseEvent e) {}
    	
    }

	/**
	 * Diese Klasse setzt die Parameter für die Menuauswahl und setzt
	 * den Text im JTextField.
	 *
	 */
	class MenuAktion implements ActionListener {
		
		@Override
		public void actionPerformed(final ActionEvent e) {
			
			// bei Menuauswahl open, Paramter neu setzen
			if (e.getActionCommand() == open.getText()) {
				insertPoint = false;
				deletePoint = false;
				movePoint 	= false;
				nonePoint 	= false;
				openP 		= true;
				newP 		= false;
				saveP 		= false;
				text.setText("Graph:" + e.getActionCommand()); 
				
				if (jfc.showOpenDialog(open) == JFileChooser.APPROVE_OPTION) {
					File file = jfc.getSelectedFile();
					System.out.println("mit Datei arbeiten");
				}
			}
			
			// bei Menuauswahl new, Paramter neu setzen
			else if (e.getActionCommand() == neu.getText()) {
				insertPoint = false;
				deletePoint = false;
				movePoint 	= false;
				nonePoint 	= false;
				openP 		= false;
				newP 		= true;
				saveP 		= false;
				text.setText("Graph:" + e.getActionCommand());
				//ArrayList<Punkt>kreise = (ArrayList) circle;
				//System.out.println(kreise);
				kreise.clear();
				zeichnenFenster.repaint();			
			}
			
			// bei Menuauswahl save, Paramter neu setzen
			else if (e.getActionCommand() == save.getText()){
				insertPoint = false;
				deletePoint = false;
				movePoint 	= false;
				nonePoint 	= false;
				openP 		= false;
				newP 		= false;
				saveP 		= true;
				text.setText("Graph:" + e.getActionCommand());
				System.out.println(kreise.get(0));
				
				PunkteXMLWrite meineDB = new PunkteXMLWrite();
				PunkteToXML meinePunkte = new PunkteToXML(kreise);
				
				File f = new File("data/meinePunkte2.xml");
				try {
					meineDB.writePunkteToXML(meinePunkte, f);
				}
				catch (Exception err) {
					System.out.println(err.getMessage());
					System.out.println("Fehler Auslesen");
				}
			
				/*if (jfc.showSaveDialog(save) == JFileChooser.APPROVE_OPTION) {
					File file = jfc.getSelectedFile();
					
					System.out.println("save");
				}*/
			}
			
			// bei Menuauswahl insert, Paramter neu setzen
			else if (e.getActionCommand() == insert.getText()){
				insertPoint = true;
				deletePoint = false;
				movePoint 	= false;
				nonePoint 	= false;
				openP 		= false;
				newP 		= false;
				saveP 		= false;
				text.setText("Vertex:" + e.getActionCommand());
			}
			
			// bei Menuauswahl delete, Paramter neu setzen
			else if (e.getActionCommand() == delete.getText()){
				insertPoint = false;
				deletePoint = true;
				movePoint 	= false;
				nonePoint 	= false;
				openP 		= false;
				newP 		= false;
				saveP 		= false;
				text.setText("Vertex:" + e.getActionCommand());          
			}
			
			// bei Menuauswahl move, Paramter neu setzen
			else if (e.getActionCommand() == move.getText()){
				insertPoint = false;
				deletePoint = false;
				movePoint 	= true;
				nonePoint 	= false;
				openP 		= false;
				newP 		= false;
				saveP 		= false;
				text.setText("Vertex:" + e.getActionCommand());
			}
			
			// bei Menuauswahl none, Paramter neu setzen
			else if (e.getActionCommand() == none.getText()){
				insertPoint = false;
				deletePoint = false;
				movePoint 	= false;
				nonePoint 	= true;
				openP 		= false;
				newP 		= false;
				saveP 		= false;
				text.setText("none");
			}

		}

	}
	
	/**
	 * MyGraphEditor starten
	 * @param args ignoriert
	 */
	public static void main(final String[] args){
    	
        new MyGraphEditor();
        
    }
	
}
```

und den dazugehörigen anderen Klassen:

```
package PflichtAufgabe3;

import java.awt.Point;
import java.util.*;

import javax.xml.bind.annotation.*;


@XmlRootElement(name ="Knoten") 
public class PunkteToXML {
	
	private ArrayList<Point> kreise;

	public PunkteToXML() {}
	
	public PunkteToXML(ArrayList<Point> kreise) {
		
		this.kreise = kreise;
		
	}
	
	@XmlElement(name = "koordinaten")
	public ArrayList<Point> getKreise() {
		
		return kreise;
		
	}

	public void setKreise(ArrayList<Point> kreise) {
		
		this.kreise = kreise;
		
	}
}
```

und


```
import javax.xml.bind.*;


import java.io.*;
import java.util.*;


public class PunkteXMLWrite{
	
		/**
		 * Methode zum Schreiben von Punkten in Datei im XML-Format
		 * @param punkteToXML die zu schreibenden Punkte
		 * @param file die Datei in die die Punkte im XML-Format geschrieben werden sollen
		 * @throws JAXBException
		 */
		public void writePunkteToXML(PunkteToXML punkteToXML, File file) throws JAXBException {
			
			JAXBContext jc = JAXBContext.newInstance(PunkteToXML.class);
			Marshaller m = jc.createMarshaller();
			m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
			m.marshal(punkteToXML, file);
			
		}
		
		/**
		 * Methode zur Rekonstuktion von Punkten aus einer Datei im XML-Format
		 * @param file die Datei mit Punkten im XML-Format
		 * @return ein Objekt mit den in file gelesenen Adressen
		 * @throws JAXBException
		 */
		public PunkteToXML readPunkteToXML(File file) throws JAXBException {
			
			JAXBContext jc = JAXBContext.newInstance(PunkteToXML.class);
			Unmarshaller u = jc.createUnmarshaller();
			return (PunkteToXML) u.unmarshal(file);
			
		}
		
}
```

habe ich das Ziel nicht erreicht. Folgendes kam dabei heraus:
[XML]<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Knoten>
    <koordinaten>
        <x>325</x>
        <y>107</y>
        <location>
            <x>325</x>
            <y>107</y>
            <location>
                <x>325</x>
                <y>107</y>
                <location>
                    <x>325</x>
                    <y>107</y>
                    <location>
                        <x>325</x>
                        <y>107</y>
                        <location>
                            <x>325</x>
                            <y>107</y>
                            <location>
<x>325</x>
<y>107</y>
<location>
    <x>325</x>
    <y>107</y>
    <location>
        <x>325</x>
        <y>107</y>
        <location>
            <x>325</x>
            <y>107</y>
            <location>
                <x>325</x>
                <y>107</y>
                <location>
                    <x>325</x>
                    <y>107</y>
                    <location>
                        <x>325</x>
                        <y>107</y>
                        <location>
                            <x>325</x>
                            <y>107</y>
                            <location>
<x>325</x>
<y>107</y>
<location>
    <x>325</x>
    <y>107</y>
    <location>
        <x>325</x>
        <y>107</y>
        <location>
            <x>325</x>
            <y>107</y>
            <location>
                <x>325</x>
                <y>107</y>
                <location>
                    <x>325</x>
                    <y>107</y>
                    <location>
                        <x>325</x>
                        <y>107</y>
                        <location>
                            <x>325</x>
                            <y>107</y>
                            <location> usw[/XML]
und die passende NegativMeldung auf Eclipse:

```
Exception in thread "AWT-EventQueue-0" java.lang.StackOverflowError
	at java.lang.Integer.toString(Unknown Source)
	at java.lang.String.valueOf(Unknown Source)
	at com.sun.xml.internal.bind.DatatypeConverterImpl._printInt(Unknown Source)
	at com.sun.xml.internal.bind.v2.model.impl.RuntimeBuiltinLeafInfoImpl$18.print(Unknown Source)
	at com.sun.xml.internal.bind.v2.model.impl.RuntimeBuiltinLeafInfoImpl$18.print(Unknown Source)
	at com.sun.xml.internal.bind.v2.model.impl.RuntimeBuiltinLeafInfoImpl$StringImpl.writeText(Unknown Source)
	at com.sun.xml.internal.bind.v2.runtime.LeafBeanInfoImpl.serializeBody(Unknown Source)
	at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.childAsXsiType(Unknown Source)
	at com.sun.xml.internal.bind.v2.runtime.property.SingleElementNodeProperty.serializeBody
```

was fehlt mir?? ich benutze ja 
	
	
	
	





```
import java.awt.Point;
```
 und das möchte ich gern beibehalten...


----------



## eRaaaa (25. Jun 2011)

Scheinbar gibts da einige Probleme mit der Point.class, denn dort ist ja auch die Methode getLocation enthalten, was JAXB dann auch als Element benutzt, dort steht wiederum drin:
return new Point(x, y);
was dann halt zu der Rekursivität führt (nehme ich jetzt an, wissen tu ichs nicht 100%)

Was du machen könntest wäre einen XMLAdapter mit einem Art Wrapper für die Point-Klasse zu benutzen:
Adapter:

```
class PointAdapter extends XmlAdapter<WrapperPoint, Point> {

	@Override
	public Point unmarshal(WrapperPoint v) throws Exception {
		return new Point(v.getxKoord(),v.getyKoord());
	}

	@Override
	public WrapperPoint marshal(Point v) throws Exception {
		return new WrapperPoint(v.x, v.y);
	}
}
```
Wrapper:

```
class WrapperPoint{
	private int xKoord;
	private int yKoord;
	
	public WrapperPoint(){}
	
	public WrapperPoint(int x, int y){
		xKoord = x;
		yKoord = y;
	}

	/**
	 * @return the xKoord
	 */
	public int getxKoord() {
		return xKoord;
	}

	/**
	 * @param xKoord the xKoord to set
	 */
	public void setxKoord(int xKoord) {
		this.xKoord = xKoord;
	}

	/**
	 * @return the yKoord
	 */
	public int getyKoord() {
		return yKoord;
	}

	/**
	 * @param yKoord the yKoord to set
	 */
	public void setyKoord(int yKoord) {
		this.yKoord = yKoord;
	}
	
}
```

In deiner PunkteToXML Klasse dann noch an der Liste den Adapter registrieren mit

```
@XmlElement(name = "koordinaten")
	@XmlJavaTypeAdapter(PointAdapter.class) // <----------------
	public ArrayList<Point> getKreise() {
```


----------



## MarioK (26. Jun 2011)

Hallo Basti,
warum weisst du immer alles ??  .... Könntest du zu den beiden Klassen noch ein paar Worte verlieren, bzw kurz beschreiben was die beiden Klassen letztendlich machen ??

Ergebnis jetzt (habe 6 Punkte in der Zeichenfläche mit "insert" eingefügt, bei "save" wurde dann folgende .xml Datei erzeugt):

[XML]<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Knoten>
    <koordinaten>
        <xKoord>321</xKoord>
        <yKoord>84</yKoord>
    </koordinaten>
    <koordinaten>
        <xKoord>349</xKoord>
        <yKoord>219</yKoord>
    </koordinaten>
    <koordinaten>
        <xKoord>590</xKoord>
        <yKoord>160</yKoord>
    </koordinaten>
    <koordinaten>
        <xKoord>588</xKoord>
        <yKoord>349</yKoord>
    </koordinaten>
    <koordinaten>
        <xKoord>435</xKoord>
        <yKoord>408</yKoord>
    </koordinaten>
    <koordinaten>
        <xKoord>571</xKoord>
        <yKoord>436</yKoord>
    </koordinaten>
</Knoten>[/XML]


----------



## eRaaaa (26. Jun 2011)

Also der Adapter macht nichts anderes als zu sagen, was beim marshalling oder unmarshalling passieren soll/ wie die Klassen/Objekte behandelt werden sollen. D.h. du sagst also quasi wie die awt.Point Klasse ins XML geschrieben werden soll. Du bekommst ja ein Point Objekt übergeben, du könntest jetzt auch einfach sagen 
return v.x+" "+v.y;

dann würde eben nicht 

       <xKoord>349</xKoord>
        <yKoord>219</yKoord>

sondern nur 349 219 (ohne die Tags etc.) rausgeschrieben.
Man könnte also _grob_ auch schreiben return "<xKoord>"+v.x+"</xKoord>\n<yKoord>"+v.y+"</yKoord>";
um das gleiche Ergebnis zu erzielen (was aber leider nicht so ganz funktionierte mit den  Tags ;D)
Also habe ich einfach eine neue Klasse erstellt die eig. fast gleich mit der awt.Point Klasse ist, nur eben NUR x und y hält und sich somit durch JAXB auch so verhält wie von dir gewünscht. Ich sage dann also quasi einfach nur, hey wenn du ein Point-Objekt schreiben willst, erstelle ein WrapperPoint Objekt und benutzt das 
Eig. nicht so ganz hübsch, gibt evtl. auch bessere Alternativen, aber so genau habe ich mich damit selbst noch nicht beschäftigt!
Beim unmarshalling dann das gleiche nur rückwärts, er bekommt ein WrapperPoint und macht daraus dann eben wieder ein awt.Point damit du den wie gewohnt benutzen kannst. Vllt solltest du dir ja dann doch überlegen direkt nur die neue Point-Klasse zu verwenden anstelle des awt.Points, k.a. :bahnhof:


----------

