javafx.beans.property.SimpleSetProperty add geht nicht

takidoso

Erfahrenes Mitglied
Hallo und Halli,
irgendwie scheine ich was nicht zu verstehen, vermutlich ist es ein Handhabungsproblem.
Ich möchte etwas als SimpleSetProperty verwenden und darin Elemente dort hinzufügen. Dafür ist, soweit ich es verstehe, die add() Routine zuständig, wie es nunmal bei Collections sein sollte. leider bekomme ich jedoch während der Laufzeit eine java.lang.UnsupportedOperationException

hier der Stacktrace selbst:
Code:
Exception in Application init method
Exception in thread "main" java.lang.RuntimeException: Exception in Application init method
	at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:398)
	at com.sun.javafx.application.LauncherImpl.access$000(LauncherImpl.java:47)
	at com.sun.javafx.application.LauncherImpl$1.run(LauncherImpl.java:115)
	at java.lang.Thread.run(Thread.java:724)
Caused by: java.lang.UnsupportedOperationException
	at java.util.AbstractCollection.add(AbstractCollection.java:252)
	at javafx.beans.binding.SetExpression.add(SetExpression.java:292)
	at de.cmk.jfx.Test2$NamedPolygon.addReachable(Test2.java:235)
	at de.cmk.jfx.Test2.figureOutNeighbourhoods(Test2.java:162)
	at de.cmk.jfx.Test2.loadContentFromYaml(Test2.java:95)
	at de.cmk.jfx.Test2.init(Test2.java:40)
	at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:296)
	... 3 more

Hier mein Versuch der Verwendung:
Java:
package de.cmk.jfx;



import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javafx.application.Application;
import javafx.beans.property.SimpleSetProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.geometry.Point2D;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.shape.Polygon;
import javafx.stage.Stage;

import org.yaml.snakeyaml.Yaml;



public class Test2 extends Application
{

	private List <NamedPolygon> m_sektors              = new ArrayList<NamedPolygon>();
	private Point2D             m_mostDistantPoint     = null;
	
	public void init() throws Exception
	{
		Map<String,String> namedParms = getParameters().getNamed();
		File               mapFile    = new File(namedParms.get("mapfile"));
		loadContentFromYaml(mapFile);
	}
	
	
	public void start(Stage stage) throws Exception
	{
		Group root = new Group();
		for (NamedPolygon sektor : m_sektors)
		{
			root.getChildren().add(sektor);
			
			sektor.setOnMouseEntered(new EventHandler<MouseEvent>()
			{
				@Override
				public void handle(MouseEvent mouseEvent)
				{
					NamedPolygon[] sektors = ((NamedPolygon) mouseEvent.getSource()).getReachables();
					for (NamedPolygon sektor : sektors)
					{
						sektor.setFill(Color.BLACK);
					}
				}
			});
			
			sektor.setOnMouseExited(new EventHandler<MouseEvent>()
			{
				@Override
				public void handle(MouseEvent mouseEvent)
				{
					NamedPolygon[] sektors = ((NamedPolygon) mouseEvent.getSource()).getReachables();
					for (NamedPolygon sektor : sektors)
					{
						sektor.setFill(sektor.getColor());
					}
				}
			});
			
		}
		
		Scene scene = new Scene(root, m_mostDistantPoint.getX(), m_mostDistantPoint.getY());
		stage.setTitle("Polygon Sample");
		stage.setScene(scene);
		stage.show();
	}
	
	
	
    @SuppressWarnings("unchecked")
	public void loadContentFromYaml(File sectorMapFile) throws FileNotFoundException, MalformedURLException
    {
    	Yaml yaml = new Yaml();
    	
    	Iterable<?> l = yaml.loadAll(new FileReader(sectorMapFile));
    	Map<String, Object> mainMap = (Map<String, Object>)l.iterator().next();
    	unmarshalContent(mainMap);
    	figureOutNeighbourhoods();
    	
        //setDirty(false);
        calcMostDistantPoint();
    }
	
    
    /**
     * deserialisiert den Inhalt der vorhaer in eine Map gelesen wurde
     * Diese Routine ist, ähnlich wie ihr Gegenstück marshalContent, dazu gedacht
     * abgeleiteten Klassen die Möglichkeit zu geben durch erweiterndes Überschreiben
     * weitere Informationen einzubringen
     * @param mainMap
     * @throws MalformedURLException
     */
    @SuppressWarnings("unchecked")
	protected void unmarshalContent(Map<String, Object> mainMap) throws MalformedURLException
    {
    	/* TODO
    	String urlStr = (String)mainMap.get("artworkimage");
    	if (urlStr!=null && urlStr.length()>0)
    	{
    		loadPlanBild(new URL(urlStr));
    	}
    	*/
    	Map<String, Map<String, Object>> sectorMap = (Map<String, Map<String, Object>>)mainMap.get("countries");
    	for (String name : sectorMap.keySet())
    	{
    		NamedPolygon sec = new NamedPolygon(name, sectorMap);
    		m_sektors.add(sec);
    		//sec.addPropertyChangeListener(this);
    	}
    }
    
    protected void calcMostDistantPoint()
    {
    	double x = 0;
    	double y = 0;
    	
    	for (NamedPolygon sektor : m_sektors)
    	{
    		ObservableList<Double> points = sektor.getPoints();
    		
    		
    		for (int i=0; i<points.size(); i++)
    		{
    			x = Math.max(points.get(i), x);
    			y = Math.max(points.get(++i), y);
    		}
    	}
    	
    	m_mostDistantPoint = new Point2D(x,y);
    }
    
	
    public void figureOutNeighbourhoods()
    {
    	for (int i=0; i<m_sektors.size(); i++)
		{
			NamedPolygon poly1 = m_sektors.get(i);
			for (int j=0; j<m_sektors.size(); j++)
			{
				if (i!=j)
				{		
					NamedPolygon poly2 = m_sektors.get(j);
					if (poly1.intersects(poly2))
					{
						poly1.addReachable(poly2);
					}
				}
			}
			
		}
    }
    
    
	public static void main(String... args)
	{
		launch(args);
	}
	
	
	
	public class NamedPolygon extends Polygon
	{
		final static public String _NAME    = "name";
		final static public String _POLYGON = "polygon";
		final static public String _COLOR   = "color";
		
		private SimpleStringProperty            m_name       = new SimpleStringProperty();
		
		private SimpleSetProperty<NamedPolygon> m_reachables = new SimpleSetProperty<NamedPolygon>();
		
		private Color                           m_color      = Color.WHITESMOKE;
		
		
		public NamedPolygon(String name, Map<String, Map<String, Object>> sectorMap)
		{
			m_name.set(name);
			retrieve (sectorMap);
		}
		
		public Color getColor()
		{
			return m_color;
		}
		
		public String getName()
		{
			return m_name.get();
		}
		
		public Map<String,Object> retrieve(Map<String, Map<String,Object>> sectorMap)
		{
			Map<String,Object> propsMap = sectorMap.get( m_name.get());
			
			@SuppressWarnings("unchecked")
			List<Double> pointCoord = (List<Double>) propsMap.get(_POLYGON);
			Long x;
			
			x = ((Number) propsMap.get(_COLOR)).longValue();
			if (x!=null)
			{
				java.awt.Color colorAWT;
				colorAWT = new java.awt.Color(x.intValue());
				m_color  = Color.rgb(colorAWT.getRed(), colorAWT.getGreen(), colorAWT.getBlue(), 0.7);
			}
			
			getPoints().addAll(pointCoord);
			setFill(m_color);
			setStrokeWidth(1);
			setStroke(Color.BLACK);
			

			return propsMap;
		}
		
		
		public void addReachable(NamedPolygon p)
		{
			m_reachables.add(p);
		}
		
		public NamedPolygon [] getReachables()
		{
			return m_reachables.toArray(new NamedPolygon[m_reachables.size()]);
		}
		
		
		/**
		 * Returns true if this polygon intersects the specified polygon.
		 *
		 * @param otherPoly  the polygon to test for intersection.
		 * @return true if the two polygons intersect, false otherwise.
		 *
		 */
		public boolean intersects(Polygon otherPoly)
		{
			int thisPointAmount  = getPoints().size();
			int otherPointAmount = otherPoly.getPoints().size();
			
			for (int thisLin = 0; thisLin < thisPointAmount; thisLin++)
			{
				Point2D  thisLinePoint1 = new Point2D(thisLin, ++thisLin);
				Point2D  thisLinePoint2 = new Point2D((thisLin + 1 ) % thisPointAmount, ( thisLin + 2 ) % thisPointAmount);
				
				for (int otherLin = 0; otherLin < otherPointAmount; otherLin++)
				{
					Point2D  otherLinePoint1 = new Point2D(otherLin, ++otherLin);
					Point2D  otherLinePoint2 = new Point2D((otherLin + 1 ) % otherPointAmount, ( otherLin + 2 ) % otherPointAmount);
					
					if (linesIntersect(thisLinePoint1, thisLinePoint2, otherLinePoint1, otherLinePoint2))
					{
						return true;
					}
				}
			}
			return false;

		}

		/**
		 * Tests whether two line segments intersect.
		 *
		 * @param p1  the first point of the first line segment.
		 * @param p2  the second point of the first line segment.
		 * @param p3  the first point of the second line segment.
		 * @param p4  the second point of the second line segment.
		 * @return true if the two segments intersect.
		 */
		public final boolean linesIntersect(Point2D p1, Point2D p2, Point2D p3, Point2D p4)
		{
			// From Graphics Gems II
			double r, s, rsDenom, rNumer;
			double dp1yp3y, dp4xp3x, dp1xp3x, dp4yp3y, dp2xp1x, dp2yp1y;
			dp1yp3y = p1.getY() - p3.getY();
			dp4xp3x = p4.getX() - p3.getX();
			dp1xp3x = p1.getX() - p3.getX();
			dp4yp3y = p4.getY() - p3.getY();
			dp2xp1x = p2.getX() - p1.getX();
			dp2yp1y = p2.getY() - p1.getY();
			rsDenom = dp2xp1x * dp4yp3y - dp2yp1y * dp4xp3x;
			rNumer = ( dp1yp3y * dp4xp3x - dp1xp3x * dp4yp3y );
			// if rsDenom is zero, lines are parallel
			// if rNumer is also zero, lines are collinear
			if (rsDenom == 0)
				if (rNumer != 0)
					return false;
				else
				{
					// Check for overlap
					double hix1, hiy1, lox1, loy1;
					if (p1.getX() > p2.getX())
					{
						hix1 = p1.getX();
						lox1 = p2.getX();
					}
					else
					{
						hix1 = p2.getX();
						lox1 = p1.getX();
					}
					if (p1.getY() > p2.getY())
					{
						hiy1 = p1.getY();
						loy1 = p2.getY();
					}
					else
					{
						hiy1 = p2.getY();
						loy1 = p1.getY();
					}
					// Check intervals
					return !( ( ( lox1 > p3.getX() && lox1 > p4.getX() ) || ( hix1 < p3.getX() && hix1 < p4.getX() ) ) || ( ( loy1 > p3.getY() && loy1 > p4.getY() ) || ( hiy1 < p3.getY() && hiy1 < p4.getY() ) ) );
				}
			r = rNumer / rsDenom;
			s = ( dp1yp3y * dp2xp1x - dp1xp3x * dp2yp1y ) / rsDenom;
			return !( r < 0 || r > 1 || s < 0 || s > 1 );
		}

		
		
/*		
		public void setPolygon(Polygon polygon)
		{	
			m_polygon = polygon;
		}
		
		public Polygon getPolygon()
		{
			return m_polygon;
		}
*/		
	}
	
}
Ich habe schon dermaßen gegoogelt doch leider finde ich kein Beispiel für SimpleSetProprety.
Habe ich irgendwas hier vergessen?

Füchsachdienliche Hinweise bin ich echt dankbar,

Takidoso
 
Ich habe in einem anderen Forum mich mit jemandem austauschen können, der mir die Philosophie dieser Properties genauer erklären konnte. Da mir die Java-Docs dann doc etwas zu spärlich waren.

Demnach wird bei unterlassener Angabe eines Sets, welches ein ObservableSet sein muss, z.B. nur durc di eVErwendung des Default-Constructors ein unveränderbares Set zurückgegeben.
Mit setValue()
Hier ein Testbeispiel
Java:
import java.util.Set;
import java.util.TreeSet;

import javafx.beans.property.SetProperty;
import javafx.beans.property.SimpleSetProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableSet;
import javafx.collections.SetChangeListener;
import javafx.collections.SetChangeListener.Change;

public class SetPropertyTest
{

	public static void main(String[] args)
	{
		final ObservableSet<String> mySet = FXCollections.observableSet("one", "two", "three");
		final SetProperty<String> setProperty = new SimpleSetProperty<>(mySet);
		setProperty.addListener(new ChangeListener<ObservableSet<String>>()
		{

			@Override
			public void changed(ObservableValue<? extends ObservableSet<String>> observable, ObservableSet<String> oldSet,
					ObservableSet<String> newSet)
			{
				System.out.printf("ChangeListener on setProperty detected change from %n%s to %n%s%n", oldSet, newSet);
			}
		});
		setProperty.addListener(new SetChangeListener<String>()
		{

			@Override
			public void onChanged(Change<? extends String> change)
			{
				System.out.println("SetChangeListener on setProperty detected change; set is now\n" + setProperty.getValue());
				showSetChanges(change);
			}
		});
		mySet.addListener(new SetChangeListener<String>()
		{

			@Override
			public void onChanged(Change<? extends String> change)
			{
				System.out.println("SetChangeListener on mySet detected change; set is now\n" + mySet);
				showSetChanges(change);
			}
		});
		System.out.println("Adding element to mySet");
		mySet.add("four");
		System.out.println("Adding element to setProperty");
		setProperty.add("five");
		System.out.println("Changing value of setProperty");
		ObservableSet<String> anotherSet = FXCollections.observableSet("a", "b", "c");
		setProperty.set(anotherSet);
		
		Set<String> hSet = new TreeSet<String>();  // hier mal ein Treeset, um etwas sortiert zu haben
		hSet.add("aa");
		hSet.add("ac");
		hSet.add("ab");
		
		anotherSet = FXCollections.observableSet(hSet);
		setProperty.setValue(anotherSet);
		
		for (String str : setProperty.get())
		{
			System.out.print("\""+str+"\",");
		}
		System.out.println();
		
		SimpleSetProperty<String> setProperty2 = new SimpleSetProperty<String>(hSet, "lala"); // das ist hier einfach falsch verstenden
		try
		{
			for (String str : setProperty2.get())
			{
				System.out.print("\""+str+"\",");
			}
			System.out.println();
		}
		catch (Exception ex)
		{
			System.out.println("erwartetes Problem: "+ex);
		}
		
		// So ist es richtig! new Object ist hie rnur ein Dummy welches nrmalerweise 
		// ein BEan darstellen wüde, welches das hie rkonkrete Set beinhaltet.
		// soweit ich es verstanden hatte aus Gründen einer möglichen Reflektion
		setProperty2 = new SimpleSetProperty<String>(new Object(), "lala", FXCollections.observableSet(hSet));
		for (String str : setProperty2.get())
		{
			System.out.print("\""+str+"\",");
		}
		System.out.println();
		
	}

	private static void showSetChanges(Change<? extends String> change)
	{
		if (change.wasAdded())
		{
			System.out.println("Added " + change.getElementAdded());
		}
		if (change.wasRemoved())
		{
			System.out.println("Removed " + change.getElementRemoved());
		}
	}
}
Ausgabe:
Code:
Adding element to mySet
ChangeListener on setProperty detected change from 
[three, two, four, one] to 
[three, two, four, one]
SetChangeListener on setProperty detected change; set is now
[three, two, four, one]
Added four
SetChangeListener on mySet detected change; set is now
[three, two, four, one]
Added four
Adding element to setProperty
ChangeListener on setProperty detected change from 
[three, two, five, four, one] to 
[three, two, five, four, one]
SetChangeListener on setProperty detected change; set is now
[three, two, five, four, one]
Added five
SetChangeListener on mySet detected change; set is now
[three, two, five, four, one]
Added five
Changing value of setProperty
ChangeListener on setProperty detected change from 
[three, two, five, four, one] to 
[b, c, a]
SetChangeListener on setProperty detected change; set is now
[b, c, a]
Removed three
SetChangeListener on setProperty detected change; set is now
[b, c, a]
Removed two
SetChangeListener on setProperty detected change; set is now
[b, c, a]
Removed five
SetChangeListener on setProperty detected change; set is now
[b, c, a]
Removed four
SetChangeListener on setProperty detected change; set is now
[b, c, a]
Removed one
SetChangeListener on setProperty detected change; set is now
[b, c, a]
Added b
SetChangeListener on setProperty detected change; set is now
[b, c, a]
Added c
SetChangeListener on setProperty detected change; set is now
[b, c, a]
Added a
ChangeListener on setProperty detected change from 
[b, c, a] to 
[aa, ab, ac]
SetChangeListener on setProperty detected change; set is now
[aa, ab, ac]
Removed b
SetChangeListener on setProperty detected change; set is now
[aa, ab, ac]
Removed c
SetChangeListener on setProperty detected change; set is now
[aa, ab, ac]
Removed a
SetChangeListener on setProperty detected change; set is now
[aa, ab, ac]
Added aa
SetChangeListener on setProperty detected change; set is now
[aa, ab, ac]
Added ab
SetChangeListener on setProperty detected change; set is now
[aa, ab, ac]
Added ac
"aa","ab","ac",
erwartetes Problem: java.lang.NullPointerException
"aa","ab","ac",
 
Zurück