8. 8.1 Der vollständige Temperaturregelungs-Simulator Definition des Simulators Der vollständige TemperaturregelungsSimulator • Wir haben bisher die grundlegenden Konzepte von JavaBeans behandelt. • Nun können wir die Einzelbeispiele zu einem Ganzen zusammensetzen. • Danach beschäftigen wir uns mit weiteren spezielleren Themen der JavaBeans anhand dieser Beispiele. • Wir wollen einen Simulator erstellen, der unsere Temperaturquelle und das Thermometer kombiniert. Definition des Simulators Grobe Anforderungsdefinition: • Der Simulator soll die Umgebungstemperatur des Systems überwachen und ändern. • Es wird ein Mechanismus benötigt, die Wunschtemperatur einstellen zu können. • Die Umgebungstemperatur soll in Celsius und in Fahrenheit dargestellt werden können. • Die ganze Simulation muß serialisierbar sein. Daraus ergeben sich die folgenden Anforderungen für die wichtigsten Komponenten des Simulators. - 169 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 8. 8.1 Der vollständige Temperaturregelungs-Simulator Definition des Simulators Temperatur Objekt • Es stellt die Umgebungstemperatur dar. Die anderen Komponenten des Simulators überwachen es oder ändern seine Temperatur. • Der Wert der Temperatur muß, am besten durch ein anders Objekt (Thermostat), darstellbar sein. • Andere Objekte müssen die Anforderung stellen können, die Temperatur zu ändern. Thermostat Objekt • Es stellt dem Anwender die Temperatur in Celsius oder Fahrenheit dar. • Der Anwender kann mit Hilfe des Thermostats die Wunschtemperatur eingeben, ebenfalls in Celsius oder Fahrenheit. • Da das Thermostat Objekt die augenblickliche Umgebungstemperatur und die Wunschtemperatur des Anwenders kennt, weiß es auch, ob die Umgebungstemperatur verändert werden muß. - 170 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 8. 8.1 Der vollständige Temperaturregelungs-Simulator Definition des Simulators Heiz- und Kühl-Objekte • Diese Objekte verändern die Umgebungstemperatur. - Jedes der Objekte hat eine bestimmte Temperatur. - Wenn sie arbeiten, senden sie in bestimmten Abstände Impulse mit ihrer Temperatur aus. • Sie wissen nicht selbst, wann sie aktiv sein müssen. Stattdessen hängen sie von anderen Objekten ab, die ihnen sagen, wann sie laufen müssen. Ereignisse • Der Thermostat weiß, wann Ist- und Soll-Temperatur differieren. - Also muß er das Heiz- oder Kühl-Objekt starten. - Ist die gewünschte Temperatur erreicht, muß er das Objekt wieder stoppen. • Wenn das Heiz- oder Kühl-Objekt läuft, sendet es TemperaturImpulse in festen Intervallen aus. Das Temperatur-Objekt reagiert darauf. • Andererseits muß der Thermostat wissen, wenn das TemperaturObjekt seine Eigenschaft ändert, damit er diese anzeigen und ggf. Heiz- oder Kühl-Objekt kontrollieren kann. Thermostat Temperature Change Request Service Turned On or Off Temperature Temperature Pulse Heating or Cooling Device - 171 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 8. 8.2 Der vollständige Temperaturregelungs-Simulator Implementierung des Simulators Implementierung des Simulators Wir beginnen mit den Event-Klassen (keien Abhängigkeiten von anderen Klassen des Systems). Dann Listeners-Interfaces. Dann die Klassen der aktiven Objekte. Temperatur-Impuls Events package BeansBook.Simulator; // class definition for the temperature pulse event public class TemperaturePulseEvent extends java.util.EventObject { // the pulse temperature protected double theTemp; // constructor public TemperaturePulseEvent(Object source, double t) { // pass the source to the superclass super(source); } } // save the temperature theTemp = t; // return the pulse temperature public double getPulseTemperature() { return theTemp; } Um solche Ereignisse empfangen zu können, muß eine Klasse die Schnittstelle TemperaturePulseListener implementieren: - 172 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 8. 8.2 Der vollständige Temperaturregelungs-Simulator Implementierung des Simulators TemperaturePulseListener Interface package BeansBook.Simulator; // interface for temperature pulse listener public interface TemperaturePulseListener extends java.util.EventListener { } // event handler method for temperature pulse public void temperaturePulse(TemperaturePulseEvent evt); Service Request Events Läßt ein Gerät (Heiz- oder Kühlgerät) sich an- oder abschalten: package BeansBook.Simulator; // the service request event class public class ServiceRequestEvent extends java.util.EventObject { // true if the request is to start the service protected boolean bStart; // constructor public ServiceRequestEvent(Object source, boolean start) { // call the superclass constructor super(source); } // save the value of the start flag bStart = start; // determine if the request is to start the service public boolean isStart() { // return the start value return bStart; } } Wird Thermostat erzeugt und geschickt, wenn dieser feststellt, daß zwischen Ist- und Soll-Temperatur ein Unterschied herrscht. - 173 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 8. 8.2 Der vollständige Temperaturregelungs-Simulator Implementierung des Simulators Listener Schnittstellen Zwei verschiedene Listener-Schnittstellen, eine für das Heizgerät und eine für das Kühlgerät. Damit ist der Empfänger sicher, daß er nur die für ihn bestimmten Ereignisse erhält. package BeansBook.Simulator; // the interface definition for heating request listeners public interface HeatingRequestListener extends java.util.EventListener { // the heating request event handler public void heatingRequest(ServiceRequestEvent evt); } // the interface definition for cooling request listeners public interface CoolingRequestListener extends java.util.EventListener { // the cooling request event handler public void coolingRequest(ServiceRequestEvent evt); } Der Ereignistyp ist in beiden Fällen derselbe. - 174 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 8. 8.2 Der vollständige Temperaturregelungs-Simulator Implementierung des Simulators Property Change Events • Die Temperature Eigenschaft des Temperature Objektes ist als bound property sinnvoll. - So kann z.B. der Thermostat von jeder Änderung erfahren. • Auch eine constrained property ist nützlich. - Es ist nicht sinnvoll, daß die Solltemperatur höher eingestellt wird, als das Heizgerät als eigene Temperatur hat. - Falls das versucht wird, muß das Heiz-Objekt ein Veto einlegen. - 175 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 8. 8.2 Der vollständige Temperaturregelungs-Simulator Implementierung des Simulators Klasse Temperature (1/3) Repräsentiert die Umgebungstemperatur (Ist-Temperatur): package BeansBook.Simulator; import java.beans.*; // the temperature bean class definition public class Temperature implements TemperaturePulseListener, // listens for temperature pulses java.io.Serializable // declare Serializable { // support object for bound property listeners protected PropertyChangeSupport boundSupport; // the current temperature protected double theTemperature = 22.0; // constructor public Temperature() { // construct the support object boundSupport = new PropertyChangeSupport(this); } // add a property change listener public void addPropertyChangeListener( PropertyChangeListener l) { // defer to the support object boundSupport.addPropertyChangeListener(l); } // remove a property change listener public void removePropertyChangeListener( PropertyChangeListener l) { // defer to the support object boundSupport.removePropertyChangeListener(l); } - 176 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 8. 8.2 Der vollständige Temperaturregelungs-Simulator Implementierung des Simulators Klasse Temperature (2/3) // get the value of the Temperature property public double getTemperature() { return theTemperature; } // set the value of the Temperature property public void setTemperature(double t) { // don't bother if the value didn't change if (t == theTemperature) return; // save the old value Double old = new Double(theTemperature); // save the new value theTemperature = t; } // fire the property change event boundSupport.firePropertyChange("Temperature", old, new Double(t)); - 177 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 8. 8.2 Der vollständige Temperaturregelungs-Simulator Implementierung des Simulators Klasse Temperature (3/3) // handle a temperature pulse event public void temperaturePulse(TemperaturePulseEvent evt) { // get the pulse temperature double p = evt.getPulseTemperature(); // get the current temp double c = getTemperature(); } } // if the pulse temperature is greater than the // current temperature if (p > c) { // only change if the difference is more than 1 if ((p - c) >= 1.0) { // add 1 to the current temperature setTemperature(c + 1.0); } } else if (p < c) { // pulse less than current temp // only change if the difference is more than 1 if ((c - p) >= 1.0) { // subtract 1 from the current temperature setTemperature(c - 1.0); } } - 178 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 8. 8.2 Der vollständige Temperaturregelungs-Simulator Implementierung des Simulators Heiz- und Kühl-Klassen (1/7) • Verhalten sich identisch. - Besitzen nur unterschiedliche Temperaturen. - Wir verwenden eine Basisklasse TemperatureModifier. - Sämtliches gemeinsames Verhalten wird hier implementiert. - Später spezialiseren wir zu Heiz- und Kühl-Klassen. • Um Instanzen dieser Klassen darstellen zu können, konstruieren wir sie als Nachfahren von Label. • Da wir unterschiedliche Threads für diese Klassen haben möchten, implemetieren sie das Interface Runnable. • Bei der Serialisierung müssen wir selbst eingreifen: Wir müssen den Thread nach der Wiederherstellung neu starten und die Listener müssen sorgfältig behandelt werden. - 179 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 8. 8.2 Der vollständige Temperaturregelungs-Simulator Implementierung des Simulators Heiz- und Kühl-Klassen (2/7) package BeansBook.Simulator; import import import import java.awt.*; java.beans.*; java.io.*; java.util.*; // class definition for the temperature modifier public abstract class TemperatureModifier extends Label implements Runnable { // a thread to send temperature pulses protected transient Thread thrd; // the temperature pulse listener protected transient TemperaturePulseListener listener = null; // the running state protected boolean running = false; // the rate (in msecs) that pulses are generated protected int rate = 1000; // the text to use when running protected String onText; // the text to use when not running protected String offText; // the color to use when running protected Color onColor; // the color to use when not running protected Color offColor; // the pulse temperature protected double temp; - 180 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 8. 8.2 Der vollständige Temperaturregelungs-Simulator Implementierung des Simulators Heiz- und Kühl-Klassen (3/7) public TemperatureModifier(double tmp, String onT, String offT, Color onC, Color offC) { // start the label out with the text for not running super(offT); // save the operating temperature temp = tmp; // save the various text and color values onText = onT; offText = offT; onColor = onC; offColor = offC; setBackground(offColor); // set text alignment to center setAlignment(Label.CENTER); } // start the pulse thread startThread(); // start the pulse thread private void startThread() { // create and start the pulse thread thrd = new Thread(this); thrd.start(); } - 181 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 8. 8.2 Der vollständige Temperaturregelungs-Simulator Implementierung des Simulators Heiz- und Kühl-Klassen (4/7) // write the state of the object private void writeObject(ObjectOutputStream stream) throws IOException { // defer to the default process stream.defaultWriteObject(); // Handle serializing of listeners // get a copy TemperaturePulseListener lstnr = null; synchronized (this) { lstnr = listener; } boolean bWriteNull = (lstnr == null); if (!bWriteNull) { // is it serializable? if (lstnr instanceof Serializable) { stream.writeObject(lstnr); } else { // write a null to the stream bWriteNull = true; } } } // should we write a null? if (bWriteNull) { stream.writeObject(null); } - 182 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 8. 8.2 Der vollständige Temperaturregelungs-Simulator Implementierung des Simulators Heiz- und Kühl-Klassen (5/7) // read the state of the object private void readObject(ObjectInputStream stream) throws IOException try { // defer to the default process stream.defaultReadObject(); { // read back the listener Object obj; while(null != (obj = stream.readObject())) { addTemperaturePulseListener( (TemperaturePulseListener)obj); } // start the pulse thread startThread(); } } catch (Exception e) { System.out.println(e); } // set the value of the Rate property public void setRate(int r) { rate = r; } // get the value of the Rate property public int getRate() { return rate; } - 183 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 8. 8.2 Der vollständige Temperaturregelungs-Simulator Implementierung des Simulators Heiz- und Kühl-Klassen (6/7) // set the value of the Running property public synchronized void setRunning(boolean b) { // don't bother if there is no change if (running == b) { return; } // save the new value running = b; } // set the appropriate label text and background color if (running) { setText(onText); setBackground(onColor); } else { setText(offText); setBackground(offColor); } // get the value of the Running property public boolean isRunning() { return running; } // add a unicast temperature pulse listener public synchronized void addTemperaturePulseListener(TemperaturePulseListener l) throws TooManyListenersException { // if there is already a listener, throw an exception if (listener != null) { throw new TooManyListenersException(); } } // store the listener listener = l; - 184 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 8. 8.2 Der vollständige Temperaturregelungs-Simulator Implementierung des Simulators Heiz- und Kühl-Klassen (7/7) // remove the unicast temperature pulse listener public synchronized void removeTemperaturePulseListener( TemperaturePulseListener l) { // make sure this is the listener we have if (listener == l) { listener = null; } } // the run method for the pulse thread public void run() { // loop forever while(true) { try { // sleep for the time specified by rate thrd.sleep(rate); // if there is a listener and the service is // running, then send the pulse event if (listener != null && running) { listener.temperaturePulse( new TemperaturePulseEvent(this, temp)); } } } } } catch (Exception e) { } - 185 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 8. 8.2 Der vollständige Temperaturregelungs-Simulator Implementierung des Simulators Klasse Cooler • Nun können wir den Cooler als Nachfahren implementieren. - Zusätzlich muß die Schnittstelle CoolingRequestListener implementiert werden. - Weiterhin muß die VetoableChangeListener Schnittstelle implementiert werden, damit der Anwender keine Temperatur angibt, die niedriger als die des Kühlgerätes ist. - Das VetoableChangeEvent kommt vom Thermostaten, wenn der Anwender die Solltemperatur ändert. • Der Cooler arbeitet mit einer Temperatur von 0° C. - 186 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 8. 8.2 Der vollständige Temperaturregelungs-Simulator Implementierung des Simulators Cooler Implementierung package BeansBook.Simulator; import java.awt.*; import java.beans.*; // the Cooler bean class definition public class Cooler extends TemperatureModifier implements CoolingRequestListener, VetoableChangeListener { // constructor public Cooler() { // a cooler is a temperature modifier that operates at // 0 degress celsius super(0.0, "COOLER ON", "COOLER OFF", Color.cyan, Color.yellow); } // handle a cooling request event public void coolingRequest(ServiceRequestEvent evt) { // set the Running property based on the value returned // by the isStart method from the event setRunning(evt.isStart()); } // handle a vetoable change event public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException { // only interested in ComfortTemperature if (evt.getPropertyName().equals( "ComfortTemperature")) { // get the proposed temperature Double d = (Double)evt.getNewValue(); } } // veto a temperature under 0 degrees Celsius if (d.doubleValue() < 0.0){ throw new PropertyVetoException( "Invalid Comfort Temperature", evt); } } - 187 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 8. 8.2 Der vollständige Temperaturregelungs-Simulator Implementierung des Simulators Boiler Implementierung (1/2) Fast identisch mit dem Cooler. Arbeitet bei 100°C. package BeansBook.Simulator; import java.awt.*; import java.beans.*; // the Boiler bean class definition public class Boiler extends TemperatureModifier // subclasses TemperatureModifier implements HeatingRequestListener, // listens for heating requests VetoableChangeListener // vetoes property changes { // constructor public Boiler() { // a boiler is a temperature modifier that operates at // 100 degress celsius super(100.0, "BOILER ON", "BOILER OFF", Color.red, Color.yellow); } // handle a heating request event public void heatingRequest(ServiceRequestEvent evt) { // set the Running property based on the value returned // by the isStart method from the event setRunning(evt.isStart()); } - 188 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 8. 8.2 Der vollständige Temperaturregelungs-Simulator Implementierung des Simulators Boiler Implementierung (2/2) // handle a vetoable change event public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException { // only interested in ComfortTemperature if (evt.getPropertyName().equals( "ComfortTemperature")) { // get the proposed temperature Double d = (Double)evt.getNewValue(); } } } // veto a temperature over 100 degrees Celsius if (d.doubleValue() > 100.0) { throw new PropertyVetoException( "Invalid Comfort Temperature", evt); } - 189 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 8. 8.2 Der vollständige Temperaturregelungs-Simulator Implementierung des Simulators Klasse Thermostat Kontrolliert die Gesamtoperation des Simulators: • Ermöglicht, daß Benutzer Wunschtemperatur spezifiziert • Entscheidet über An-/Abschalten der Heizung und der Kühlung • Hat Benutzerschnittstelle Gemäß der Spezifikationen benötigt er • Anzeige der augenblicklichen Temperatur • Dropdown Liste, um Einheit der Temperaturanzeige (C oder F) zu wählen • Anzeige der gewünschten Temperatur • Zwischen augenblicklicher und gewünschter Temperatur kann ebenfalls mit einer Dropdownliste hin und her geschaltet werden • Zwei Buttons (Inkrement, Dekrement), um gewünschte Temperatur um jeweils 1 Grad zu verändern (nur verfügbar, wenn Anzeige auf gewünschter Temperatur steht) Auswahl der Temperatur-Einheit << Anzeige >> Auswahl der Temperatur Anzeige - 190 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 8. 8.2 Der vollständige Temperaturregelungs-Simulator Implementierung des Simulators Thermostat Implementierung (1/16) Ähnlich einer Form, in der sich mehrere andere Interface Komponenten befinden: Nachfahre der Klasse Panel. Einige Interfaces müssen implementiert werden: • PropertyChangeListener: Themostat hört auf Temperature Property des Temperature Objektes • ActionListener: Pushbutton Ereignisse können behandelt werden. • ItemListener: Item Ereignisse der Dropdown Listen können behandelt werden. package BeansBook.Simulator; import import import import import java.awt.*; java.awt.event.*; java.beans.*; java.util.*; java.io.*; // the Thermostat bean class definition public class Thermostat extends Panel // subclasses Panel implements PropertyChangeListener, // property changes // from temperature // source ActionListener, // action events // from buttons ItemListener // item events from // choice controls { // the choice control for display units protected Choice unitsChoice; // the choice control for displaying ambient or // comfort level temperature protected Choice displayChoice; - 191 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 8. 8.2 Der vollständige Temperaturregelungs-Simulator Implementierung des Simulators Thermostat Implementierung (2/16) // button for decrementing comfort level temperature protected Button decButton; // button for incrementing comfort level temperature protected Button incButton; // label for displaying temperatures protected Label displayLabel; // flags for the NeedsCooling and NeedsHeating state protected boolean bNeedsCooling = false; protected boolean bNeedsHeating = false; // the comfort level temperature protected double comfortTemp = 22.0; // the ambient temperature from the temperature source // via property change event // there is no direct reference to the Temperature object protected double ambientTemp; // flag indicating if ambient temperature is being // displayed protected boolean bShowingAmbient = true; // flag indicating if Celsius units are being displayed protected boolean bShowingCelsius = true; // support for the constrained properties protected VetoableChangeSupport vetoSupport; // cooling and heating request listeners protected transient Vector coolingListeners; protected transient Vector heatingListeners; - 192 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 8. 8.2 Der vollständige Temperaturregelungs-Simulator Implementierung des Simulators Thermostat Implementierung (3/16) Constructor public Thermostat() { // call superclass constructor super(); // create the support object for constrained properties vetoSupport = new VetoableChangeSupport(this); // use a border layout for the constituents setLayout(new BorderLayout()); // create the unitsChoice displayChoice decButton incButton displayLabel constituents = new Choice(); = new Choice(); = new Button("<<"); = new Button(">>"); = new Label("**********"); // register as action listener for the buttons decButton.addActionListener(this); incButton.addActionListener(this); // register as item listener for the choice controls displayChoice.addItemListener(this); unitsChoice.addItemListener(this); // disable the comfort temperature buttons decButton.setEnabled(false); incButton.setEnabled(false); // add the constituents in their appropriate locations add(unitsChoice, BorderLayout.NORTH); add(displayChoice, BorderLayout.SOUTH); add(decButton, BorderLayout.WEST); add(incButton, BorderLayout.EAST); add(displayLabel, BorderLayout.CENTER); // use center alignment for the temperature display displayLabel.setAlignment(Label.CENTER); - 193 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 8. 8.2 Der vollständige Temperaturregelungs-Simulator Implementierung des Simulators Thermostat Implementierung (4/16) // add the units to the choice control unitsChoice.add("Celsius"); unitsChoice.add("Fahrenheit"); } // add the display types to the choice control displayChoice.add("Ambient"); displayChoice.add("Comfort"); actionPerformed // handle an action event public void actionPerformed(ActionEvent evt) { // change in temperature double tempDelta; // delta value for rounding double roundingDelta; // it was the decrement button if (evt.getSource() == decButton) { // reduce temp by 1 tempDelta = -1.0; // delta is 0 for rounding down roundingDelta = 0.0; } else { // it was the increment button // increase temp by 1 tempDelta = 1.0; } // delta is 1 for rounding up roundingDelta = 1.0; - 194 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 8. 8.2 Der vollständige Temperaturregelungs-Simulator Implementierung des Simulators Thermostat Implementierung (5/16) // the new proposed comfort temperature double newValue; // displaying temperatures in Celsius if (bShowingCelsius) { // just add the delta newValue = comfortTemp + tempDelta; } else { // displaying in Fahrenheit // convert to Fahrenheit, add the delta, // and convert back double t = 32.0 + ((comfortTemp * 9.0) / 5.0); t += tempDelta; newValue = (t - 32.0) * 5.0 / 9.0; } // the old value object for firing the vetoable // change event Double old = new Double(comfortTemp); try { // fire the event vetoSupport.fireVetoableChange("ComfortTemperature", old, new Double(newValue)); // if we get this far we can make the change synchronized (this) { comfortTemp = newValue; } } catch (PropertyVetoException e) { // the change was vetoed by a listening object, but // if we have a fractional part, we could try // rounding the value to use a whole number double wholePart = (double)old.longValue(); if ((old.doubleValue() - wholePart) == 0.0) { // we can't make a change return; } - 195 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 8. 8.2 Der vollständige Temperaturregelungs-Simulator Implementierung des Simulators Thermostat Implementierung (6/16) // attempt to alter the comfort temperature using the // whole part of the old value and the rounding delta newValue = wholePart + roundingDelta; try { // fire the event vetoSupport.fireVetoableChange( "ComfortTemperature", old, new Double(newValue)); } catch (PropertyVetoException ee) { // we couldn’t make this change either return; } } // we can go ahead and change it now synchronized (this) { comfortTemp = wholePart + roundingDelta; } // set the needs cooling and needs heating states, // comparing the ambient and comfort level temperatures setNeedsCooling((ambientTemp > comfortTemp) && ((ambientTemp - comfortTemp) >= 1.0)); setNeedsHeating((ambientTemp < comfortTemp) && ((comfortTemp - ambientTemp) >= 1.0)); } // redisplay the temperature displayTemp(); - 196 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 8. 8.2 Der vollständige Temperaturregelungs-Simulator Implementierung des Simulators Thermostat Implementierung (7/16) displayTemp // display the temperature protected void displayTemp() { // temporary temperature value double t; if (bShowingAmbient) { // use the ambient temperature t = ambientTemp; } else { // use the comfort level temperature t = comfortTemp; } // if not using Celsius, convert to Fahrenheit if (!bShowingCelsius) { t = 32.0 + ((t * 9.0) / 5.0); } } // display whole number part of the temperature Double d = new Double(t); displayLabel.setText(new Long( d.longValue()).toString()); - 197 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 8. 8.2 Der vollständige Temperaturregelungs-Simulator Implementierung des Simulators Thermostat Implementierung (8/16) itemStateChanged // handle item state change events public void itemStateChanged(ItemEvent evt) { if (evt.getSource() == displayChoice && evt.getStateChange() == ItemEvent.SELECTED) { // determine the newly selected item string String sel = (String)evt.getItem(); if (sel.equals("Comfort")) { // showing comfort level, not ambient bShowingAmbient = false; // enable comfort level buttons decButton.setEnabled(true); incButton.setEnabled(true); // display the temperature displayTemp(); } else if (sel.equals("Ambient")) { // showing ambient temperature bShowingAmbient = true; // disable the comfort level buttons decButton.setEnabled(false); incButton.setEnabled(false); // display the temperature displayTemp(); } } - 198 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 8. 8.2 Der vollständige Temperaturregelungs-Simulator Implementierung des Simulators Thermostat Implementierung (9/16) else if (evt.getSource() == unitsChoice && evt.getStateChange() == ItemEvent.SELECTED) { // a new temperature units item has been selected Choice pp = (Choice)evt.getSource(); // determine the newly selected item string String sel = (String)evt.getItem(); if (sel.equals("Celsius")) { // showing Celsius bShowingCelsius = true; // display the temperature displayTemp(); } else if (sel.equals("Fahrenheit")) { // showing Fahrenheit, not Celsius bShowingCelsius = false; } } } // display the temperature displayTemp(); add/removeHeatingRequestListener // add a heating request listener public synchronized void addHeatingRequestListener(HeatingRequestListener l) { // add a listener if it is not already registered if (heatingListeners == null) { heatingListeners = new Vector(); } } if (!heatingListeners.contains(l)) { heatingListeners.addElement(l); } - 199 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 8. 8.2 Der vollständige Temperaturregelungs-Simulator Implementierung des Simulators Thermostat Implementierung (10/16) // remove the heating request listener public synchronized void removeHeatingRequestListener(HeatingRequestListener l) { // remove it if it is registered if (heatingListeners != null) { heatingListeners.removeElement(l); } } add/removeCoolingRequestListener // add a cooling request listener public synchronized void addCoolingRequestListener(CoolingRequestListener l) { // add a listener if it is not already registered if (coolingListeners == null) { coolingListeners = new Vector(); } } if (!coolingListeners.contains(l)) { coolingListeners.addElement(l); } // remove the cooling request listener public synchronized void removeCoolingRequestListener(CoolingRequestListener l) { // remove it if it is registered if (coolingListeners != null) { coolingListeners.removeElement(l); } } getComfortTemperature // return the comfort temperature public synchronized double getComfortTemperature() { return comfortTemp; } - 200 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 8. 8.2 Der vollständige Temperaturregelungs-Simulator Implementierung des Simulators Thermostat Implementierung (11/16) setNeedsCooling // set the needs cooling state private void setNeedsCooling(boolean b) { // do nothing if there is no change if (b == bNeedsCooling) { return; } bNeedsCooling = b; // nothing else to do if collection isn’t allocated yet if (coolingListeners == null) { return; } // fire the cooling service request ServiceRequestEvent evt = new ServiceRequestEvent( this, bNeedsCooling); // make a copy of the listener object vector so that it // cannot be changed while we are firing events Vector v; synchronized(this) { v = (Vector) coolingListeners.clone(); } } // fire the event to all listeners int cnt = v.size(); for (int i = 0; i < cnt; i++) { CoolingRequestListener client = (CoolingRequestListener)v.elementAt(i); client.coolingRequest(evt); } - 201 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 8. 8.2 Der vollständige Temperaturregelungs-Simulator Implementierung des Simulators Thermostat Implementierung (12/16) setNeedsHeating // set the needs heating state private void setNeedsHeating(boolean b) { // do nothing if there is no change if (b == bNeedsHeating) { return; } bNeedsHeating = b; // nothing else to do if collection isn’t allocated yet if (heatingListeners == null) { return; } // fire the heating service request ServiceRequestEvent evt = new ServiceRequestEvent(this, bNeedsHeating); // make a copy of the listener object vector so that it // cannot be changed while we are firing events Vector v; synchronized(this) { v = (Vector) heatingListeners.clone(); } } // fire the event to all listeners int cnt = v.size(); for (int i = 0; i < cnt; i++) { HeatingRequestListener client = (HeatingRequestListener)v.elementAt(i); client.heatingRequest(evt); } - 202 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 8. 8.2 Der vollständige Temperaturregelungs-Simulator Implementierung des Simulators Thermostat Implementierung (13/16) add/removeVetoableChangeListene r // add vetoable change listener public void addVetoableChangeListener(VetoableChangeListener l) { vetoSupport.addVetoableChangeListener(l); } // remove vetoable change listener public void removeVetoableChangeListener(VetoableChangeListener l) { vetoSupport.removeVetoableChangeListener(l); } propertyChange // handle a property change from another source public void propertyChange(PropertyChangeEvent evt) { // check for a Temperature property change if (evt.getPropertyName().equals("Temperature")) { // modify the ambient temperature Double d = (Double)evt.getNewValue(); ambientTemp = d.doubleValue(); // update the bNeedsCooling and bNeedsHeating // variables setNeedsCooling((ambientTemp > comfortTemp) && ((ambientTemp - comfortTemp) >= 1.0)); setNeedsHeating((ambientTemp < comfortTemp) && ((comfortTemp - ambientTemp) >= 1.0)); } // display the temperature displayTemp(); } - 203 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 8. 8.2 Der vollständige Temperaturregelungs-Simulator Implementierung des Simulators Thermostat Implementierung (14/16) writeObject private void writeObject(ObjectOutputStream stream) throws java.io.IOException { // perform default writing first stream.defaultWriteObject(); // if we have allocated coolingListeners if (coolingListeners != null) { // clone the vector in case one is added or removed Vector v = null; synchronized (this) { v = (Vector) coolingListeners.clone(); } int cnt = v.size(); for(int i = 0; i < cnt; i++) { // get the listener element from the collection CoolingRequestListener l = (CoolingRequestListener)v.elementAt(i); } } // if the listener is serializable, // write it to the stream if (l instanceof Serializable) { stream.writeObject(l); } // a null object marks the end of the // cooling request listeners stream.writeObject(null); - 204 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 8. 8.2 Der vollständige Temperaturregelungs-Simulator Implementierung des Simulators Thermostat Implementierung (15/16) // if we have allocated heatingListeners if (heatingListeners != null) { // clone the vector in case one is added or removed Vector v = null; synchronized (this) { v = (Vector) heatingListeners.clone(); } int cnt = v.size(); for(int i = 0; i < cnt; i++) { // get the listener element from the collection HeatingRequestListener l = (HeatingRequestListener)v.elementAt(i); } } } // if the listener is serializable, // write it to the stream if (l instanceof Serializable) { stream.writeObject(l); } // a null object marks the end of the heating // request listeners stream.writeObject(null); - 205 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 8. 8.2 Der vollständige Temperaturregelungs-Simulator Implementierung des Simulators Thermostat Implementierung (16/16) readObject // handle the reading of the object state private void readObject(ObjectInputStream stream) throws java.io.IOException { try { stream.defaultReadObject(); Object l; // get the cooling request listeners while(null != (l = stream.readObject())) { addCoolingRequestListener( (CoolingRequestListener)l); } // get the heating request listeners while(null != (l = stream.readObject())) { addHeatingRequestListener( (HeatingRequestListener)l); } } } catch (ClassNotFoundException e) { throw new IOException(); } } - 206 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 8. 8.3 Der vollständige Temperaturregelungs-Simulator Der Simulator als Applet Der Simulator als Applet (1/2) Ein Applet, das die bisher entwickelten Beans verwendet. • Das Applet erzeugt Instanzen von Temperature, Thermostat, Boiler und Cooler und • erzeugt die notwendigen Verbindungen. All dies geschieht in der init() Methode. import import import import java.applet.*; java.awt.*; BeansBook.Simulator.*; java.beans.*; // the SimulatorSample applet class public class SimulatorSample extends Applet { // the applet init method public void init() { // use a flow layout, with components centered, and // a 20 pixel separation between components setLayout(new FlowLayout(FlowLayout.CENTER, 20, 20)); // create a temperature Temperature t = new Temperature(); // create a thermostat Thermostat th = new Thermostat(); // create a cooler Cooler c = new Cooler(); // create a boiler Boiler b = new Boiler(); // add the thermostat, cooler, and boiler to the applet add(th); add(c); add(b); - 207 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 8. 8.3 Der vollständige Temperaturregelungs-Simulator Der Simulator als Applet Der Simulator als Applet (2/2) // make the thermostat a property change listener of the // temperature object t.addPropertyChangeListener(th); // make the cooler a cooling request listener of the // thermostat object th.addCoolingRequestListener(c); // make the boiler a heating request listener of the // thermostat object th.addHeatingRequestListener(b); // make the boiler and cooler vetoable change listeners // of the thermostat object th.addVetoableChangeListener(b); th.addVetoableChangeListener(c); try { // make the temperature object a temperature pulse // listener of the cooler and boiler objects c.addTemperaturePulseListener(t); b.addTemperaturePulseListener(t); } catch (java.util.TooManyListenersException e) { } // start the temperature object at 0 degrees t.setTemperature(0); } } - 208 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 8. 8.3 Der vollständige Temperaturregelungs-Simulator Speichern in einer JAR Datei Speichern in einer JAR Datei (1/2) Damit können alle Bestandteile gemeinsam weitergegeben werden und sie können auch in der BeanBox verwendet werden. Manifest-Datei Manifest-Version: 1.0 Name: BeansBook/Simulator/Boiler.class Java-Bean: True Name: BeansBook/Simulator/Cooler.class Java-Bean: True Name: BeansBook/Simulator/CoolingRequestListener.class Java-Bean: False Name: BeansBook/Simulator/HaetingRequestListener.class Java-Bean: False Name: BeansBook/Simulator/ServiceRequestEvent.class Java-Bean: False Name: BeansBook/Simulator/Temperature.class Java-Bean: True Name: BeansBook/Simulator/TemperatureModifier.class Java-Bean: False Name: BeansBook/Simulator/TemperaturePulseEvent.class Java-Bean: False Name: BeansBook/Simulator/TemperaturePulseListener.class Java-Bean: False Name: BeansBook/Simulator/Thermostat.class Java-Bean: True - 209 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 8. 8.3 Der vollständige Temperaturregelungs-Simulator Speichern in einer JAR Datei Speichern in einer JAR Datei (1/2) Die Datei Simulator.mft und die Class-Datei des Applets SimulatorSample.class müssen sich in einem Verzeichnis oberhalb BeansBook befinden. Die JAR Datei wird erzeugt: jar cvfm Simulator.jar Simulator.mft BeansBook/Simulator/*.class In der BeanBox Damit kann der Simulator nun auch in der BeanBox benutzt werden. - 210 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 9. Introspection Introspection Der Java Reflection Mechanismus kann Informationen über • Properties, • Methoden und • behandelte Ereignisse der Beans sammeln, in dem der Programmierer sich an bestimmte Namenskonventionen (sog. design patterns) hält. Eventuell reicht aber diese Information nicht aus oder man möchte nicht alle so verfügbaren Informationen veröffentlichen. Oder man möchte sich nicht an die Namenskonventionen halten müssen. Manchmal sind Informationen auch nicht durch Namenskonventionen darstellbar (z.B. ein graphische Icon für eine Bean). Der Mechanismus, Informationen über eine Bean explizit zur Verfügung zu stellen, heißt Introspection. Dafür wird eine Klasse erzeugt, die speziell zur Beschreibung der Bean dient. - 211 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 9. 9.1 Introspection Das BeanInfo Interface Das BeanInfo Interface (1/2) Diese Klasse muß das java.beans.BeanInfo Interface implementieren. Dessen Methoden: Methode getAdditionalBeanInfo() getBeanDescriptor() getDefaultEventIndex() getDefaultPropertyIndex() getEventSetDescriptors() getIcon() getMethodDescriptors() getPropertyDescriptors() Beschreibung Gibt zusätzliche BeanInfo Objekte zurück, die für diese Bean relevant sind Gibt das Bean Descriptor Objekt Gibt den Default Event Index Gibt den Default Property Index Gibt die Event Set Deskriptoren Gibt den zu der Bean gehörigen Icon Gibt die Method Deskriptoren Gibt die Property Deskriptoren Reflection: Dieser Mechanismus anlysiert die Bean, um Properties, Methoden und Ereignisse herauszufinden. Introspection: Ein Meta-Objekt stellt die Informationen zur Verfügung. Vorteil der Introspection: • Metainformation muß zur Laufzeit nicht mitgetragen werden. • Run-Time Version ist nicht zur Entwicklung einsetzbar. Die BeanInfo Klasse hat ihre eigenen Namenskonventionen: Ihr Name ist der Klassenname der Bean mit „BeanInfo“ angehängt. Also z.B. Xyz und XyzBeanInfo - 212 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 9. 9.1 Introspection Das BeanInfo Interface Das BeanInfo Interface (2/2) Normalerweise wird die BeanInfo Klasse in demselben Package wie das der Bean gesucht. Man kann jedoch noch einen BeanInfo SearchPath angeben: getBeanInfoSearchPath() setBeanInfoSearchPath() der Klasse java.beans.Introspector Nachteil: Zwei unabhängige Klassen müssen konsistent gehalten werden. Jede der Methoden des BeanInfo Interfaces kann null zurückgeben: • Bedeutet: Es gibt diese Information nicht: Verwende Reflection. Klasse java.beans.SimpleBeanInfo. • Liefert immer null zurück • Man kann dann einen Nachfahren dieser Klasse definieren, der die gewünschten Methoden überschreibt. - 213 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 9. 9.1 Introspection Das BeanInfo Interface BeanInfo und Superklassen • Bei Reflection werden auch die Superklassen analysiert. • Dabei hat die einmal bei einer der Subklassen gefundene Information Vorrang vor später bei einer Superklasse gefundenen Informationen. • Bei Introspection wird die durch die BeanInfo Klasse über ein bestimmtes Feature zur Verfügung gestellte Information nicht mehr bei Vorfahrenklassen gesucht. • Informiert BeanInfo z.B. über Events, so müssen alle Events beschrieben werden, auch die, die die Superklassen verwenden. • Es gibt die Möglichkeit, von einem BeanInfo Objekt auf die BeanInfo Objekte der Superklassen zu verweisen. - 214 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 9. Introspection 9.1 Das BeanInfo Interface 9.1.1 Feature Descriptor Feature Descriptor • Alle Aspekte einer Bean, die durch Introspection beschrieben werden können, können als Features der Bean angesehen werden. • Diese haben gemeinsame Eigenschaften, die durch die Klasse java.beans.FeatureDescriptor beschrieben werden. Methoden des Features Bedeutung setName() Name getName() setDisplayName() getDisplayName() setShortDescription() getShortDescription() setExpert() isExpert() setHidden() isHidden() setValue() getValue() attributeNames() „Freundlicherer“ Name, z.B. für Anzeigen Beschreibung Feature nur für Experten Modus Wenn true: Zeige dies Feature nur im Entwicklungswerkzeug Zugriff auf assoziatives Array mit Schlüsseln (Keys) und Werten Liefere eine Enumeration, um durch alle Keys zu iterieren - 215 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 9. Introspection 9.1 Das BeanInfo Interface 9.1.2 Bean Descriptor Bean Descriptor java.beans.BeanDescriptor beschreibt die Klassen, die die Bean und den Bean Customizer implementieren. Konstruktoren der Klasse: public BeanDescriptor(Class beanClass) public BeanDescriptor(Class beanClass, Class customizerClass) • Parameter der ersten Version: Klasse, die die zugehörige Bean implementiert. • Zusätzlicher Parameter der zweiten Version: Klasse, die den Customizer der zugehörigen Bean implementiert. Für unser Thermostat Objekt würde die Erzeugung der BeanDescriptor Instanz wie folgt aussehen: BeanDescriptor bd = new BeanDescriptor(BeansBook.Simulator.Thermostat.class); Benutzer des BeanDescriptor Objektes würden die Methoden • getBeanClass() oder • getCustomizerClass() verwenden, um Referenzen auf die entsprechenden Klassen zu erhalten. - 216 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 9. Introspection 9.1 Das BeanInfo Interface 9.1.2 Bean Descriptor Bean Descriptor Beispiel Definition einer BeanInfo Klasse für die Thermostat Klasse: package BeansBook.Simulator; import java.beans.*; public class ThermostatBeanInfo extends SimpleBeanInfo { // return the bean descriptor public BeanDescriptor getBeanDescriptor() { // create the bean descriptor BeanDescriptor bd = new BeanDescriptor(Thermostat.class); // set the display name bd.setDisplayName("Simulated Thermostat"); } } // return the object return bd; - 217 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 9. Introspection 9.1 Das BeanInfo Interface 9.1.3 Icons Icons (1/2) Jede Bean kann einen zugehörigen Icon besitzen, z.B. um im Entwicklungstool in einer Beans-Palette darzustellen. Randbedingungen: • GIF Format • 16 x 16 oder 32 x 32 Pixels • Farbe oder monochrom Nehmen wir an, wir haben einen 16 x 16 farbigen GIF Icon: thermostat.gif. • Diese Datei befindet sich im selben Verzeichnis wie die zugehörige Klasse (muß nicht sein, ist aber praktisch). Damit der Introspector dieses Feature finden kann, implementieren wir die Methode getIcon(). Sie wird mit einem Parameter aufgerufen, der angibt, welche der vier möglichen Varianten gesucht wird. - 218 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 9. Introspection 9.1 Das BeanInfo Interface 9.1.3 Icons Icons (1/2) Fortsetzung der obigen Klassendefinition: // get an icon public java.awt.Image getIcon(int iconKind) { // we're only supporting 16x16 color icon if (iconKind == BeanInfo.ICON_COLOR_16x16) { // load the thermostat.gif file java.awt.Image img = loadImage("thermostat.gif"); } } // return the image return img; // return null for all other icon formats return null; loadImage() wird von SimpleBeanInfo zur Verfügung gestellt. Mögliche BeanInfo Konstanten: ICON_COLOR_16x16 ICON_COLOR_32x32 ICON_MONO_16x16 ICON_Monoi_32x32 Ergänzung von Simulator.mft: Name: BeansBook/Simulator/ThermostatBeanInfo.class Java-Bean: False Name: BeansBook/Simulator/thermostat.gif Java-Bean: False Erzeugung der JAR Datei: jar cvfm Simulator.jar Simulator.mft BeansBook/Simulator/*.class BeansBook/Simulator/*.gif In BeanBox: Zusätzlich der Icon und als Display Name „Simulated Thermostat“. Alle sonstigen Informationen wie zuvor über Reflection. - 219 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 9. Introspection 9.1 Das BeanInfo Interface 9.1.4 Property Descriptors Property Descriptors • Explizite Informationen über Properties. • Basis ist die Klasse java.beans.PropertyDescriptor. • Drei verschiedene Konstruktoren für drei Varianten. Standard Namenskonvention Eigenschaft hat get-/set-Methoden entsprechend den StandardNamenskonventionen. public PropertyDescriptor(String propertyName, Class beanClass) throws IntrospectionException; 1. Parameter: Name der Property 2. Parameter: Klasse, die die Bean implementiert Exception passiert, wenn Property nicht so implementiert ist. - 220 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 9. Introspection 9.1 Das BeanInfo Interface 9.1.4 Property Descriptors Property Descriptors: Standard Namenskonventionen Beispiel für Klasse Temperature (1/2) package BeansBook.Simulator; import java.beans.*; import java.lang.reflect.*; public class TemperatureBeanInfo extends SimpleBeanInfo { // return a bean descriptor for the Temperature object public BeanDescriptor getBeanDescriptor() { // create an instance of BeanDescriptor BeanDescriptor bd = new BeanDescriptor(Temperature.class); bd.setDisplayName("Temperature Source"); return bd; } - 221 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 9. Introspection 9.1 Das BeanInfo Interface 9.1.4 Property Descriptors Property Descriptors: Standard Namenskonventionen Beispiel für Klasse Temperature (2/2) // return the property descriptors public PropertyDescriptor[] getPropertyDescriptors() { try { // create a property descriptor for the // Temperature property PropertyDescriptor pd = new PropertyDescriptor("Temperature", Temperature.class); // the Temperature property is bound pd.setBound(true); // the Temperature property is not constrained pd.setConstrained(false); // is default // create the property descriptor array and return it PropertyDescriptor[] pda = { pd }; return pda; } } } catch (IntrospectionException e) { return null; } Die Klasse zeigt hier an, daß sie propertyChange Events feuert, aber keine vetoableChange Events. - 222 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 9. Introspection 9.1 Das BeanInfo Interface 9.1.4 Property Descriptors Property Descriptors: Nichtstandard Methodennamen Der Konstruktor: public PropertyDescriptor(String propertyName, Class beanClass; String getterName, String setterName) throws IntrospectionException; Für den Methodennamen kann auch null angegeben werden (z.B. bei read-only Zugriff). Hier ist man nun frei in der Namenswahl der Zugriffsmethoden. Obiges Beispiel im Ausschnitt public PropertyDescriptor[] getPropertyDescriptors() { try { PropertyDescriptor pd = new PropertyDescriptor("Temperature", Temperature.class, "returnTemperature", "assignTemperature); pd.setBound(true); pd.setConstrained(false); // is default PropertyDescriptor[] pda = { pd }; return pda; } } catch (IntrospectionException e) { return null; } - 223 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 9. Introspection 9.1 Das BeanInfo Interface 9.1.4 Property Descriptors Property Descriptors: Nichtstandard Methoden-Objekte Anstelle von • Namen als Strings werden • Objekte der Klasse java.lang.reflect.Method übergeben. Dabei muß nicht mehr die Implementierungsklasse übergeben werden, da diese in den Methoden-Objekten enthalten ist: public PropertyDescriptor(String Method Method throws - 224 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences propertyName, getter, setter) IntrospectionException; - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 9. Introspection 9.1 Das BeanInfo Interface 9.1.4 Property Descriptors Property Descriptors: Nichtstandard Methoden-Objekte Obiges Beispiel neu (1/2) public PropertyDescriptor[] getPropertyDescriptors() try { // get the Temperature class Class c = Temperature.class; { // get the get method for the Temperature property Method getter = c.getMethod("returnTemperature", null); // create the parameters array for the set method // of the Temperature property type double Class[] params = { java.lang.Double.TYPE }; // get the set method Method setter = c.getMethod("assignTemperature", params); PropertyDescriptor pd = new PropertyDescriptor("Temperature", getter, setter); pd.setBound(true); pd.setConstrained(false); // is default PropertyDescriptor[] pda = { pd }; return pda; } - 225 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 9. Introspection 9.1 Das BeanInfo Interface 9.1.4 Property Descriptors Property Descriptors: Nichtstandard Methoden-Objekte Obiges Beispiel neu (2/2) } catch (IntrospectionException e) { return null; } catch (NoSuchMethodException e) { return null; } catch (SecurityException e) { return null; } Die Klasse java.beans.PropertyDescriptor stellt Methoden zur Verfügung, die es erlauben, • die get- und set-Methoden einer bestimmten Property, • ihren Datentyp und, • ob sie bound oder constrained ist, zu erhalten. - 226 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 9. Introspection 9.1 Das BeanInfo Interface 9.1.5 Default Property Default Property Kann in einigen Entwicklungsumgebungen von Interesse sein. • Zuweisen eines Wertes an den Namen der Bean, ohne eine Property anzugeben. • Dann erhält die Default Property diesen Wert. Implementierung durch die Methode getDefaultPropertyIndex(). Beispiel public PropertyDescriptor[] getPropertyDescriptors() { try { PropertyDescriptor color = new PropertyDescriptor( "Color", Example.class); PropertyDescriptor color = new PropertyDescriptor( "Font", Example.class); PropertyDescriptor color = new PropertyDescriptor( "Size", Example.class); PropertyDescriptor[] pda = { colr, font, size }; return pda; } } catch (IntrospectionException e) { return null } public int getDefaultPropertyIndex() { // font is the default return 1; } - 227 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 9. Introspection 9.1 Das BeanInfo Interface 9.1.6 Deskriptoren für Indizierte Properties Deskriptoren für Indizierte Properties Beschrieben durch Instanz von java.beans.IndexedPropertyDescriptor, ein Nachfahre von java.beans.PropertyDescriptor. Indizierte Properties haben Methoden, um auf eine bestimmte oder auf alle Properties zuzugreifen. Erste Konstruktorvariante Falls die Standard Namenskonventionen für normale und indizierte Properties eingehalten wurden: public IndexedPropertyDescriptor(String propertyName, Class beanClass) throws IntrospectionException; Als Beispiel nehmen wir unsere Klasse WatchList. Da sie strikt nach den Namenskonventionen erstellt wurde, können wir die folgende Implementierung verwenden: public PropertyDescriptor[] getPropertyDescriptors() { try { IndexedPropertyDescriptor ip = new IndexedPropertyDescriptor("Stocks", WatchList.class); PropertyDescriptor[] pda = { ip }; retrun pda; } catch (IntrospectionException) { return null; } } - 228 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 9. Introspection 9.1 Das BeanInfo Interface 9.1.6 Deskriptoren für Indizierte Properties Deskriptoren für Indizierte Properties Weitere Konstruktorvarianten Die anderen Varianten sind analog wie bei den nicht-indizierten Properties: public IndexedPropertyDescriptor(String propertyName, Class beanClass, String getterName, String setterName, String indexedGetterName, String indexedSetterName) throws IntrospectionException; und public IndexedPropertyDescriptor(String propertyName, Method getter, Method setter, Method indexedGetter, Method indexedSetter) throws IntrospectionException; - 229 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 9. Introspection 9.1 Das BeanInfo Interface 9.1.7 Methoden Deskriptoren Methoden Deskriptoren • Normalerweise werden Methoden durch den Zusatz public veröffentlicht. • Mit Hilfe dieser Deskriptoren können die für einzelne Beans zur Verfügung stehenden Methoden explizit genannt werden. • Weiterhin können besser beschreibende Informationen zu den Methoden gegeben werden. Methoden werden veröffentlicht, • indem Instanzen von java.beans.MethodDescriptor erzeugt werden und • diese in einem Array durch die Methode getMethodDescriptors() der BeanInfo Klasse geliefert werden. Variante 1 Eine Methode als Parameter: public MethodDescriptor(Method method); - 230 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 9. Introspection 9.1 Das BeanInfo Interface 9.1.7 Methoden Deskriptoren Methoden Deskriptoren Variante 2 Für die Parameter der Methode, die in der Instanz von Method formal beschrieben sind, können beschreibende Informationen hinzugefügt werden. Dazu wird eine Instanz der Klasse java.beans.ParameterDescriptor verwendet. Diese Klasse ist Nachfahre von java.beans.FeatureDescriptor. Sie fügt keine weitere Informationen zur dieser Klasse hinzu. Konstruktor: public MethodDescriptor(Method method, ParameterDescriptor parameterDescriptors[]); Parameterdeskriptoren als Array. • Für jeden Parameter der Methode muß eine Beschreibung angegeben werden. Beispiel WatchList Um das zu veranschaulichen fügen wir unserer WatchList zwei Methoden hinzu: public synchronized void clearStocks(); public synchronized boolean sortStocks(int nOrder); - 231 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 9. Introspection 9.1 Das BeanInfo Interface 9.1.7 Methoden Deskriptoren Methoden Deskriptoren getMethodDescriptors public MethodDescriptor[] getMethodDescriptors() { try { Class c = WatchList.class; // method clearStocks Method clear = c.getMethod("clearStocks", null); MethodDescriptor clearDesc = new MethodDescriptor(clear); clearDesc.setShortDescription( "Clears the list of stocks"); // method sortStocks Class[] params = { java.lang.Integer.TYPE }; Method sort = c.getMethod("sortStocks", params); ParameterDescriptor pd = new ParameterDescriptor(); pd.setShortDescription("Specifies the sort order"); // array of descriptors for parameters ParameterDescriptor[] s = { pd }; MethodDescriptor sortDesc = new MethodDescriptor(sort, s); sortDesc.setShortDescription( "Sorts the list of stocks"); // construct method descriptor array MethodDescriptor[] mda = { clearDesc, sortDesc }; return mda; } } catch (SecurityException e) { return null; } catch (NoSuchMethodException e) { return null; } - 232 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 9. Introspection 9.1 Das BeanInfo Interface 9.1.7 Methoden Deskriptoren Methoden Deskriptoren Auswirkungen • Nur die hier aufgeführten Methoden der Klasse WatchList sind für Entwicklungswerkzeuge sichtbar. • Hierdurch wurden auch die Methoden von Vorfahrenklassen unsichtbar. (Wir werden einen Weg kennenlernen, um das zu beheben). • Unabhängig hiervon sind jedoch Zugriffsmethoden für Properties und Registrierungsmethoden für Ereignisse. Sie gehören zu anderen Features. • Auch durch Programmierung kann man unabhängig davon auf alle öffentlichen Methoden zugreifen. - 233 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 9. Introspection 9.1 Das BeanInfo Interface 9.1.8 Event Set Deskriptoren Event Set Deskriptoren • Veröffentlichen die Ereignisse, die eine Bean feuern kann. • Alle Ereignisse, die über dasselbe Listener Interface empfangen werden, werden in einem solchen Set zusammengefaßt. • Dieser ist eine Instanz der Klasse java.beans.EventSetDescriptor. • Die BeanInfo Klasse muß die Methode getEventSetDescriptors() implementieren, die ein Array von Event Set Deskriptoren zurückgibt. Auch hier kann der Konstruktor unterschiedliche Formen annehmen. Erste Konstruktorvariante Erwartet • die Standard Namenskonventionen für die Registrierung und Deregistrierung von Listeners sowie • ein Ereignisobjekt mit nur einem Parameter, der auch den Namenskonventionen entspricht. public EventSetDescriptor(Class sourceClass, String eventSetName, Class listenerType, String listenerMethodName) throws IntrospectionException; - 234 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 9. Introspection 9.1 Das BeanInfo Interface 9.1.8 Event Set Deskriptoren Event Set Deskriptoren Erste Konstruktorvariante Anwendbar, wenn • ein SampleListener Interface • eine einzige Methode mit einem einzigen Parameter vom Typ SampleEvent hat, und wenn • die Klasse, die die Bean implementiert, die Methoden addSampleListener() und removeSampleListener() zur Verfügung stellt. Betrachten wir als Beispiel wieder die Klasse TemperatureBeanInfo. • Das Temperature Objekt kann ein Property Change Event feuern. • Dafür können wir die Methode getEventSetDescriptors() implementieren: // return the event set descriptors public EventSetDescriptor[] getEventSetDescriptors() { try { // create the event set descriptor EventSetDescriptor ed = new EventSetDescriptor(Temperature.class, "Property Change Event", PropertyChangeListener.class, "propertyChange"); // create the descriptor array and return it EventSetDescriptor[] eda = { ed } ; return eda; } } catch (IntrospectionException e) { return null; } - 235 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 9. Introspection 9.1 Das BeanInfo Interface 9.1.8 Event Set Deskriptoren Event Set Deskriptoren Zweite Konstruktorvariante Ermöglicht mehr Flexibilität in Namen und Anzahl der Methoden in dem Listener Inteface. public EventSetDescriptor(Class sourceClass, String eventSetName, Class listenerType, String listenerMethodNames[], String addListenerMethodName, String removeListenerMethodName) throws IntrospectionException; Diese Version unseres Beispiels: public EventSetDescriptor[] getEventSetDescriptors() { try { String[] names = { "propertyChange" }; EventSetDescriptor ed = new EventSetDescriptor(Temperature.class, "Property Change Event", PropertyChangeListener.class, names, "addPropertyChangeListener", "removePropertyChangeListener"); EventSetDescriptor[] eda = { ed } ; return eda; } } catch (IntrospectionException e) { return null; } • Eventlistener Registriermethoden können beliebige Namen haben. • EventListener Interface kann mehrere Methoden enthalten. • Jedoch nur ein Parameter nach Namenskonvention. - 236 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 9. Introspection 9.1 Das BeanInfo Interface 9.1.8 Event Set Deskriptoren Event Set Deskriptoren Weitere Konstruktorvarianten Es gibt noch zwei weitere Varianten: public EventSetDescriptor(String eventSetName, Class listenerType, Method listenerMethods[], Method addListenerMethod, Method removeListenerMethod) throws IntrospectionException; und public EventSetDescriptor(String eventSetName, Class listenerType, MethodDescriptor listenerMethodDescriptors[], Method addListenerMethod, Method removeListenerMethod) throws IntrospectionException; Die EventSetDescriptor Klasse stellt Methoden zur Verfügung, • um die Listener Methoden als Instanzen von java.lang.reflect.Method oder java.beans.MethodDescriptor zu liefern und • via setUnicast() und isUnicast() unicast Ereignisse zu unterstützen. - 237 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 9. Introspection 9.1 Das BeanInfo Interface 9.1.9 Default Event Set Default Event Set Wie bei den Properties kann es wünschenswert sein, einen Event Set als Default zu spezifizieren. • Dazu muß man die Methode getDefaultEventIndex() implementieren. - Die Klasse SimpleBeanInfo liefert mit dieser Methode eine –1 zurück, was bedeutet, daß es keinen Default Event Set gibt. • Der Nutzen dieser Möglichkeit ist nicht sehr einleuchtend. - 238 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 9. 9.2 Introspection Zusätzliche BeanInfo Objekte Zusätzliche BeanInfo Objekte Informationen der Vorfahrenklassen fehlen , wenn explizite Informationen durch eine Subklasse zur Verfügung gestellt werden. • Klasse AA veröffentlicht die Property propA. • Klasse BB sei Nachfahre von AA und veröffentliche seine eigene Property propB. - Mit low-level Reflection sehen wir bei BB die beiden Properties propA und propB. • Sobald jedoch für BB eine eigene BeanInfo Klasse BBBeanInfo erzeugt wird, - die die Methode getPropertyDescriptor() implementiert, - welche ihrerseits ein Property Descriptor Array mit nur einem Element, nämlich propB liefert, sieht man nicht mehr die geerbte Property propA. Das java.beans.BeanInfo Interface enthält eine Methode getAdditionalBeanInfo(), mit deren Hilfe man weitere BeanInfo Objekte erhalten kann, die für diese Klasse relevant sind. Original-BeanInfo Instanz hat Vorrang vor den zusätzlichen. Bei den zusätzlichen: Später im Array stehende Vorrang vor früheren (Vererbungshierarchie). BeanInfo für Wurzelklasse bei Index 0. Klasse AA Klasse BB 0 BeanInfo für Klasse AA 1 BeanInfo für Klasse BB getAdditionalBeanInfo() Klasse CC Klasse CCBeanInfo - 239 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 9. Introspection 9.2 Zusätzliche BeanInfo Objekte 9.2.1 Introspector Introspector Um solche zusätzlichen BeanInfo-Klassen zu finden, kann die Klasse java.beans.Introspector benutzt werden. • Falls keine entsprechende BeanInfo Klasse vorhanden ist, verwendet Introspector den normalen Reflection Mechanismus. Beispiel einer getAdditionalBeanInfo() Methode der Klasse BBBeanInfo: public BeanInfo[] getAdditionalBeanInfo() { try { BeanInfo[] bia = { Introspector.getBeanInfo(AA.class) }; return bia; } catch (IntrospectionException e) { return null; } } - 240 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 9. Introspection 9.2 Zusätzliche BeanInfo Objekte 9.2.1 Introspector Introspector Mit Hilfe von java.beans.Introspector kann man z.B. ein ReportingTool von Beans schreiben. In BeanBox schon integriert. Ergebnis (so ähnlich): CLASS: BeansBook.Simulator.Temperature H => Hidden E => Expert [ => Indexed Property Properties: Temperature double returnTemperature/assignTemperature Event sets: Property Change Event addPropertyChangeListener/removePropertyChangeListener propertychange Methods: public void BeansBook.Simulator.Temperature. assignTemperature(double) public final void java.lang.Object.wait(long,int) public void BeansBook.Simulator.Temperature. removePropertyChangeListener (java.beans.PropertyChangeListener) public final native void java.lang.Object.notifyAll() public double BeansBook.Simulator.Temperature. returnTemperature() public final void java.lang.Object.wait() public java.lang.String java.lang.Object.toString() public final native void java.lang.Object.notify() public void BeansBook.Simulator.Temperature. addPropertyChangeListener (java.beans.PropertyChangeListener) public native int java.lang.Object.hashCode() public final native java.lang.Class java.lang.Object.getClass() public void BeansBook.Simulator.Temperature. temperaturePulse (BeansBook.Simulator.TemperaturePulseEvent) public final native void java.lang.Object.wait(long) public boolean java.lang.Object.equals(java.lang.Object) - 241 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 9. 9.3 Introspection Informationen über die Laufumgebung Informationen über die Laufumgebung Die Bean könnte auch über die Umgebung, in der sie sich gerade befindet, interesssiert sein. Sie könnte sich z.B. unterschiedlich verhalten, ob sie sich in einem Entwicklungstool oder in einer Laufumgebung befindet. Ein Entwicklungstool kann die Methode setDesignTime() der Klasse java.beans.Beans aufrufen, um der Bean dies mitzuteilen. Die Bean kann über isDesignTime() dies überprüfen. Über die analogen Methoden setGuiAvailable() und isGuiAvailable() kann die Bean feststellen, ob sie in einer graphischen Umgebung läuft. Es gibt weitere Methoden in dieser Richtung in der Schnittstelle java.beans.Visibility. Die BeanInfo Klassen des Simulators s. Webpage zur Vorlesung - 242 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 10. Property Editoren und Customizer Property Editoren und Customizers Wichtiges Feature von Komponenten • Für visuelle Entwicklungswerkzeuge. • Änderung von Eigenschaften und Verhalten der Komponenten. Benutzerschnittstelle hierfür: • Property Sheet mit allen Properties. jede Property mit ihrem Property Editor. Sind die Eigenschaften einer Komponenten komplexer, kann es sinnvoll sein, eine spezielle Schnittstelle zum Anpassen der Eigenschften zur Verfügung zu stellen: • Customizers. - 243 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 10. 10.1 Property Editoren und Customizer Property Editoren Property Editoren Visuelle Entwicklungswerkzeuge verwenden Property Sheets oder ähnliches. Siehe BeanBox. Die PropertyEditor Schnittstelle Property Editor: Klasse zum Editieren einer Eigenschaft eines bestimmten Typs. • primitiver Datentyp oder • beliebige Klasse Property Editoren müssen Schnittstelle java.beans.PropertyEditor implementieren. • Konsistentes Verhalten aller Property Editoren garantiert Wie wird ein Property Editor mit einer bestimmten Eigenschaft verknüpft? Einfachste Technik: Explizit als Teil der zugehörigen BeanInfo Klasse deklarieren. Klasse java.beans.PropertyDesciptor enthält die Methode setPropertyEditorClass(), um den zugehörigen Property Editor zu identifizieren. - 244 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 10. 10.1 Property Editoren und Customizer Property Editoren Property Editoren Demonstration der unterschiedlichen Techniken anhand der Boiler Klasse. Zunächst Basisklasse TemperatureModifier und dort die Eigenschaft Running. • In welcher BeanInfo Klasse soll der Editor deklariert werden, - in der von TemperatureModifier oder - von Boiler? • Wir wählen die Superklasse, so daß die Information für alle Nachfahren zur Verfügung steht. Zunächst konzentrieren wir uns jedoch auf den Editor. • Die Klasse soll BeansBook.Simulator.RunningEditor heißen. • Sie implementiert das Interface java.beans.PropertyEditor. • Die Klasse java.beans.PropertyEditorSupport macht das Leben wieder etwas einfacher. • Deshalb definieren wir unseren Editor direkt als Nachfahren dieser Klasse: RunningEditor (1/3) // -- The first version of the RuningEditor class package BeansBook.Simulator; import java.beans.*; public class RunningEditor extends PropertyEditorSupport { // the current state of the property protected boolean bRunning; - 245 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 10. 10.1 Property Editoren und Customizer Property Editoren Property Editoren RunningEditor (2/3) Jeder Property Editor implementiert setValue(). Ihm wird ein Object übergeben, auch wenn die Eigenschaft nur ein primitiver datentyp ist, wie hier. Mittels getValue() wird das Ergebnis zurückgegeben. // set the object being edited public void setValue(Object o) { // get the primitive data value from the object version bRunning = ((Boolean)o).booleanValue(); } // get the current value of the property public Object getValue() { // return the object version of the primitive type return new Boolean(bRunning); } Unser Editor stellt die einfachste Form des Editierens zur Verfügung, nämlich die über einen Textstring, Nach einer Änderung wird ein Property Change Event gefeuert. // get the value of the property as text public String getAsText() { if (bRunning) { // return the text that indicates running return "YES"; } else { // return the text that indicates not running return "NO"; } } - 246 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 10. 10.1 Property Editoren und Customizer Property Editoren Property Editoren RunningEditor (3/3) // set the value of the property as text public void setAsText(String s) { if (s.equals("YES")) { // the state is running bRunning = true; } else { // the state is not running bRunning = false; } } } // let any interested listeners know about the change firePropertyChange(); - 247 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 10. 10.1 Property Editoren und Customizer Property Editoren Property Editoren TemperatureModifierBeanInfo Bevor wir jetzt die Simulator.jar Datei neu erzeugen, müssen wir noch die Klasse BeansBook.Simulator.TemperatureModifierBeanInfo schreiben: package BeansBook.Simulator; import java.beans.*; import java.lang.reflect.*; public class TemperatureModifierBeanInfo extends SimpleBeanInfo { // return the property descriptors public PropertyDescriptor[] getPropertyDescriptors() { try { // create a descriptor for the Rate property PropertyDescriptor pd1 = new PropertyDescriptor("Rate", TemperatureModifier.class); // create a descriptor for the Running property PropertyDescriptor pd2 = new PropertyDescriptor("Running", TemperatureModifier.class); // specify the property editor for Running pd2.setPropertyEditorClass(RunningEditor.class); // create an array of descriptors and // return it to the caller PropertyDescriptor[] pda = { pd1, pd2 }; return pda; } } } catch (Exception e) { return null; } - 248 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 10. 10.1 Property Editoren und Customizer Property Editoren Property Editoren Editor für Running Simulator.jar neu erzeugen: • Zuvor muß Simulator.mft geändert werden. • Wir müssen das folgende hinzufügen: Name: BeansBook/Simulator/RunningEditor.class Java-Bean: False Name: BeansBook/Simulator/TemperatureModifierBeanInfo.class Java-Bean: False Der Property Editor für die Eigenschaft Running sieht dann wie folgt aus: Er stellt jetzt den Wert der booleschen Eigenschaft als Textstring dar. • So muß er auch eingegeben werden, wobei die Schreibweise natürlich genau einzuhalten ist. - 249 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 10. 10.1 Property Editoren und Customizer Property Editoren Property Editoren Auswahlfeld für Running Schöner wäre es, wenn man die richtigen Werte als Auswahl zur Verfügung gestellt bekäme. • Dazu stelle die Schnittstelle java.beans.PropertyEditor die Methode getTags() zur Verfügung, - welches ein Array von Strings zurückliefert, - die alle legale Werte der Property sind. Wir fügen also diese Methode zu BeansBook.Simulator.RunningEditor hinzu. // get the string tags public String[] getTags() { String[] s = { "YES", "NO" }; return s; } No sieht der Editor wie folgt aus: Nicht alle Eigenschaften lassen sich als Text oder eine Aufzählung darstellen. Liefert getAsText() ein null zurück, so weiß das Property Sheet daß eine Darstellung als Text nicht möglich ist. - 250 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 10. 10.1 Property Editoren und Customizer Property Editoren Property Editoren Eigene Darstellung für Running (1/2) Vollständige Übernahme der Darstellung: • Löschen der Implementierungen von getAsText() und setAsText(). • Zwei Methoden, um den Wert der Eigenschaft selbst zu „malen“. - isPaintable() sagt, ob der Editor den Wert selbst dartellen kann. Der Defult-Wert ist false. - paintValue(): - Als erster Parameter wird das java.awt.Graphics Objekt übergeben, auf das gezeichnet werden muß, - der zweite Parameter ist ein java.awt.Rectangle, welches die Größe der Fläche definiert. Änderungen in der Klasse RunningEditor: // indicate that we support paintValue public boolean isPaintable() { return true; } // implement the paintValue method public void paintValue(Graphics gfx, Rectangle box) { // the string variable that holds the string to draw String s; if (bRunning) { // if running we paint YES s = "YES"; } else { // if not running we paint NO s = "NO"; } - 251 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 10. 10.1 Property Editoren und Customizer Property Editoren Property Editoren Eigene Darstellung für Running (2/2) // set the background color to white gfx.setColor(Color.white); // fill the rectangle with the background color gfx.fillRect(box.x, box.y, box.width, box.height); // set the color to black for drawing text gfx.setColor(Color.black); // find a reasonable place to draw the string based // on the font FontMetrics fm = gfx.getFontMetrics(); int w = fm.stringWidth(s); int x = box.x + (box.width - w) / 2; int y = box.y + box.height / 2; } // draw the string that represents the value of // the property gfx.drawString(s, x, y); Dies ist aber bisher nur die Darstellung. Damit kann der Wert noch nicht geändert werden. Dazu müssen wir einen eigenen Editor zur Verfügung stellen. Daß wir das tun, zeigen wir durch die Methode supportsCustomEditor() an, die jetzt true liefern muß. Die Methode getCustomEditor() muß dann die Instanz des Editors liefern. • Das Ergebnis ist vom Typ java.awt.Component. • Also muß der Editor ein Nachfahre dieser Klasse sein (tatsächlich ist er ein Panel). • Unser Editor heißt BeansBook.Simulator.RunningPanel. - 252 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 10. 10.1 Property Editoren und Customizer Property Editoren Property Editoren Eigener Editor für Running // we support a custom editor public boolean supportsCustomEditor() { return true; } // return the custom editor public Component getCustomEditor() { // create an instance of the custom editor return new RunningPanel(this); } - 253 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 10. 10.1 Property Editoren und Customizer Property Editoren Property Editoren Der Editor für Running (1/2) • Der Editor RunningPanel ist Bestandteil desselben Packages, da er sonst nirgends eingesetzt werden soll. • Er wird als nicht public definiert und ist Bestandteil der RunningEditor.java Datei. • Als Benutzerschnittstelle werden zwei Buttons (YES und NO) verwendet: ... class RunningPanel extends Panel implements ActionListener { // create the YES button protected Button yes = new Button("YES"); // create the NO button protected Button no = new Button("NO"); // the instance of the editor that we’re working for protected RunningEditor editor; // the constructor public RunningPanel(RunningEditor ed) { // save a reference to the editor we’re working for editor = ed; // set the size of the buttons to 50 x 50 yes.setSize(50,50); no.setSize(50,50); // add the buttons to the panel add(yes); add(no); } // become an action listener for the yes and no buttons yes.addActionListener(this); no.addActionListener(this); - 254 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 10. 10.1 Property Editoren und Customizer Property Editoren Property Editoren Eigener Editor für Running (2/2) // handle the action performed event public void actionPerformed(ActionEvent evt) { if (evt.getSource() == yes) { // tell the editor about the new value editor.setValue(new Boolean(true)); } else { // tell the editor about the new value editor.setValue(new Boolean(false)); } } } // tell the editor to fire a property change event to // it’s listeners editor.firePropertyChange(); Nachdem die Datei Simulator.mft um Name: BeansBook/Simulator/RunningPanel.class Java-Bean: False ergänzt wurde sieht der Editor nun wie folgt aus: - 255 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 10. 10.1 Property Editoren und Customizer Property Editoren Der Custom Property Editor kommt in Gestalt einer modalen Dialogbox, die von der BeanBox erzeugt wird. - 256 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 10. 10.1 Property Editoren und Customizer Property Editoren Property Editoren finden Wir haben den Bezug zu dem richtigen Property Editor in der entsprechenden BeanInfo Klasse gemacht. • Aber nicht jede Bean mag eine eigene BeanInfo Klasse bereitstellen. • Geschickter wäre es, wir könnten für einen bestimmten PropertyTyp den geeigneten Editor angeben. - In der Tat funktioniert die BeanBox so. - Z.B. gibt es für alle Properties von Typ boolean einen geeigneten Editor. Wir erstellen zunächst die Klasse Running, um diesen „Datentyp“ zu definieren: package BeansBook.Simulator; public class Running { // the value of the running object protected boolean bRunning; // constructor public Running(boolean b) { // save the state bRunning = b; } } // return the value of the object public boolean value() { return bRunning; } - 257 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 10. 10.1 Property Editoren und Customizer Property Editoren Property Editoren finden Notwendige Änderungen des Simulators Durch die Einführung dieses neuen Datentyps müssen etliche Änderungen an unseren Simulatorklassen durchgeführt werden: • In der Klasse TemperatureModifier muß für die Methode setRunning() der Parametertyp von boolean auf Running geändert werden • Die Methode isRunning() derselben Klasse muß in getRunning() umbenannt und ebenfalls an den neuen Datentyp angepaßt werden. • In den Klassen Cooler und Boiler müssen die Methoden coolingRequest() und heatingRequest() auf den geänderten Datentyp umgestellt werden, da diese die Methode setRunning() der Basisklasse aufrufen. • Die Klasse RunningEditor muß geändert werden, damit dort anstelle boolean ebenfalls Running benutzt wird. Dies betrifft die methoden setValue() und getValue(). • Der Custom Property Editor RunningPanel muß ebenfalls auf Running umgestellt werden. In actionPerformed() wird setValue() aufgerufen und muß nun den geänderten Parametertyp aufnehmen. Mit diesen Änderungen funktioniert der Simulator wie zuvor. Aber das war nur die Vorbereitung. - 258 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 10. 10.1 Property Editoren und Customizer Property Editoren Einen Property Editor registrieren In der TemperatureModifierBeanInfo Klasse führen wir eine geringe Änderung durch. • Dort spezifizierten wir den Editor mit der Methode setPropertyEditorClass() für eine bestimmte Property Descriptor Variable (pd2). • Wir löschen diesen Aufruf und verwenden eine andere Methode, um den richtigen Editor zu finden. Ist der zugehörige Editor in dem entsprechenden Property Descriptor nicht definiert, so wird die Klasse java.beans.PropertyEditorManager benutzt, um den richtigen Editor zu finden. Dabei werden mehrere Techniken verwendet. • Die erste basiert auf der Registrierung eines Editors für einen bestimmten Datentyp (Klasse). • Dies geschieht mit der Methode registerEditor(). - Sie besitzt zwei Parameter: Die Klasse des Property Typs und die Klasse des Editors. - Die Deregistrierung für einen bestimmten Property Typ findet hier durch die Übergabe von null für die Editor Klasse statt(!). - 259 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 10. 10.1 Property Editoren und Customizer Property Editoren Einen Property Editor registrieren Die Registrierung gilt nur zur Laufzeit, sie wird nirgends permanent gespeichert. Wir ergänzen die Running Klasse um die Registrierung: // -- The second version of the Running class package BeansBook.Simulator; public class Running { // Initialize the class static { // register a property editor for the Running object java.beans.PropertyEditorManager.registerEditor( Running.class, RunningEditor.class); } // the value of the running object protected boolean bRunning; // constructor public Running(boolean b) { // save the state bRunning = b; } } // return the value of the object public boolean value() { return bRunning; } - 260 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 10. 10.1 Property Editoren und Customizer Property Editoren Einen Property Editor registrieren Weitere Technik Eine weitere Technik ist es, wieder bestimmte Namenskonventionen zu befolgen. • Ist für einen bestimmten Typ kein Editor registriert, sucht der Property Editor Manager in demselben Package nach einer gleichnamigen Klasse mit dem String „Editor“ angehängt. • Wir haben diese Namenskonvention offenbar schon eingehalten: - Typ Klasse: Running - Editor Klasse: RunningEditor • Wir könnten also den static Block aus der Klasse Running entfernen. Default Pfad Wird der Property Editor Manager hierbei nicht fündig, sucht er nach obiger Namenskonvention in einem Default Pfad. • Dies ist sun.beans.editors. • Hier befinden sich bereits die Default Editoren für die primitiven Datentypen sowie für java.awt.Color, java.awt.Font und java.lang.String. Dieser Pfad kann gelesen und geändert werden mit • getEditorSearchPath() und • setEditorSearchPath(). Mit letzterer Methode können auch mehrere Pfade angegeben werden (interessant für Tool-Hersteller). - 261 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 10. 10.2 Property Editoren und Customizer Customizers Customizers Benutzerschnittstelle, um eine komplette Bean anzupassen, nicht nur eine einzelne Eigenschaft. • Hierbei können auch andere Charakteristiken der Bean angepaßt werden, nicht nur die veröffentlichten Properties. • Selbst „Assistenten“ sind möglich. Voraussetzung für Customizer: BeanInfo Klasse. • Kann auch nur eine triviale sein. • Keine feste Namenskonvention für Customizerklassen. • Kein Customizer Manager, um einen Customizer für bestimmte Klassen zu registrieren. Im Prinzip kann ein Customizer auch zur Run-Time zur Verfügung getellt werden, dann vielleicht mit reduziertem Funktionsumfang. Einfachste Art, Customizer zu deklarieren: • Trivialen Bean Descriptor implementieren • Dessen Konstruktor nimmt als zweiten Parameter die Customizer Klasse auf - 262 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 10. 10.2 Property Editoren und Customizer Customizers Customizer für Boiler Klasse Über Bean Descriptor bekannt machen public BeanDescriptor getBeanDescriptor() { // create the bean descriptor object BeanDescriptor bd = new BeanDescriptor(Boiler.class, BoilerCustomizer.class); // set the display name bd.setDisplayName("Simulated Boiler"); } // return the descriptor object return bd; - 263 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 10. 10.2 Property Editoren und Customizer Customizers Customizer für Boiler Klasse Klasse BeansBook.Simulator.BoilerCustomizer Voraussetzungen • Alle Bean Customizer müssen das java.beans.Customizer Interface implementieren. Hat nur drei Methoden: - Übliche addPropertyChangeListener() und removePropertyChangeListener() Methoden, um die Änderungen den Listener Objekten mitteilen zu können. - Methode setObject() mit java.lang.Object als Parametertyp. Parameter repräsentiert die Bean, die angepaßt werden soll. • Customizer muß Nachfahre von java.awt.Component sein. - Damit kann der Customizer zu einem Panel oder einer Dialogbox hinzugefügt werden. • Er muß Default-Konstruktor zur Verfügung stellen - Er wird über einen Aufruf von newInstance() der Klasse java.lang.Class erzeugt. Anforderungen an Boiler Customizer • Die Werte der Properties Rate und Running können verändert werden. • Der Konstruktor der Klasse Boiler ruft den Konstruktor der Vorfahrenklasse mit einigen Parametern auf, die auch anpaßbar sein sollen, z.B. die Arbeitstemperatur (100 °C). - 264 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 10. 10.2 Property Editoren und Customizer Customizers Customizer für Boiler Klasse Änderungen an TemperatureModifier Arbeitstemperatur ändern: • Modifikation der Vorfahrenklasse TemperatureModifier um Methoden, um diese Eigenschaft schreiben und lesen zu können. • Da diese Methoden nicht mit Property set- und get-Methoden verwechselt werden sollen, geben wir ihnen nicht den Namenskonventionen entsprechende Namen: // set the operating temperature public void assignOperatingTemperature(double t) { temp = t; } // get the operating temperature public double retrieveOperatingTemperature() { return temp; } Weiterer Parameter ist Farbe, die die Komponente als „on“ kennzeichnet: // set the "on" color public void assignOnColor(Color c) { onColor = c; if (running) { setBackground(onColor); } } // get the "on" color public Color retrieveOnColor() { return onColor; } - 265 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 10. 10.2 Property Editoren und Customizer Customizers Customizer für Boiler Klasse Änderungen an Boiler Es kann passieren, daß, nachdem die gewünschte Umgebungstemperatur erreicht wurde, die Boiler Temperatur niedriger als diese gesetzt wird. • Selbst wenn dann die gewünschte Temperatur 1 Grad niedriger gesetzt wird, würde es zu Vetos kommen. • Dies muß verhindert werden, indem die vetoableChange() Methode der Klasse Boiler angepaßt wird: public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException { // only interested in ComfortTemperature if (evt.getPropertyName().equals("ComfortTemperature")) { // get the proposed temperature Double d = (Double)evt.getNewValue(); // get the old value Double old = (Double)evt.getOldValue(); // determine if the temperature is increasing boolean bIncreasing = (d.doubleValue() > old.DoubleValue()); } } // veto a rising temperature over the operating // temperature if (bIncreasing && d.doubleValue() > retrieveOperatingTemperature()) { throw new PropertyVetoException( "Invalid Comfort Temperature", evt); } - 266 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 10. 10.2 Property Editoren und Customizer Customizers Customizer für Boiler Klasse (1/8) Es gibt keine Support-Klasse. Wir müssen „from scratch“ beginnen. Benutzerschnittstelle • Choice Liste für Running • Text Field für Rate und die Operating Temperature • Color Property Editor, um die Darstellungsfarbe für den aktiven Bolier zu wählen Vorgänger und Schnittstellen • Klasse erweiter java.awt.Panel und implementiert java.beans.Customizer. • Implementiert die Schnittstellen java.awt.ItemListener und java.awt.ActionListener. • Ein Text Field feuert ein actionPerformed Ereignis, wenn Enter gedrückt wurde (keine Reaktion auf jede einzelne Eingabe). • PropertyChangeListener Interface wird gebraucht, um die Property Change Events von dem Color Property Editor zu behandeln. package BeansBook.Simulator; import java.awt.*; import java.awt.event.*; import java.beans.*; public class BoilerCustomizer implements Customizer, ItemListener, ActionListener PropertyChangeListener extends Panel // to be a bean customizer // to handle Choice list events // to handle action events // color property changes { - 267 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 10. 10.2 Property Editoren und Customizer Customizers Customizer für Boiler Klasse (2/8) Attribute // the boiler that we are customizing protected Boiler theBoiler; // an instance of property change support protected PropertyChangeSupport support; // the Choice list for the Running property protected Choice runningChoice = new Choice(); // a text field for the Rate property protected TextField rateField = new TextField("*******"); // a text field for the operating temperature protected TextField tempField = new TextField("*******"); // the property editor for the running color protected PropertyEditor colorEditor; Der Color Property Editor wird erst zur Laufzeit gefunden. Der Konstruktor erzeugt alle nötigen Objekte und fügt sie, falls angebracht, dem Panel zu. - 268 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 10. 10.2 Property Editoren und Customizer Customizers Customizer für Boiler Klasse (3/8) public BoilerCustomizer() { // Empty Constructor // create the instance of the support object support = new PropertyChangeSupport(this); // add a label for the Running property add(new Label("Running State: ")); // add the Running property choice list add(runningChoice); // add a label for the Rate property add(new Label("Pulse Rate: ")); // add the Rate property field add(rateField); // add a label for the operating temperature add(new Label("Operating Temp: ")); // add the operating temperature field add(tempField); // add a label for the color editor add(new Label("Running Color: ")); // find custom editor and add it to the panel colorEditor = PropertyEditorManager.findEditor( java.awt.Color.class); add(colorEditor.getCustomEditor()); // add the choice strings to the Running choice runningChoice.add("NO"); runningChoice.add("YES"); // become an item listener for the Choice list runningChoice.addItemListener(this); // become an action listener for the edit fields rateField.addActionListener(this); tempField.addActionListener(this); } - 269 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 10. 10.2 Property Editoren und Customizer Customizers Customizer für Boiler Klasse (4/8) setObject() Das setObject() übergebene Objekt ist die anzupassende Bean, also unsere Boiler Instanz. Wir fragen die einzelnen Eigenschaften und Zustände ab und setzen die Benutzerschinittstellen entsprechend. public void setObject(Object o) { // save a reference to the bean we're customizing theBoiler = (Boiler)o; // get the state of the Running property and select it // in the Choice list if (theBoiler.getRunning().value()) { runningChoice.select("YES"); } else { runningChoice.select("NO"); } // put the current value of the Rate property // into the field int rate = theBoiler.getRate(); rateField.setText(String.valueOf(rate)); // put the current value of the operating temperature // into the field double temp = theBoiler.retrieveOperatingTemperature(); tempField.setText(String.valueOf(temp)); // hookup the color editor colorEditor.setValue(theBoiler.retrieveOnColor()); } // prepare to receive property change Events colorEditor.addPropertyChangeListener(this); - 270 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 10. 10.2 Property Editoren und Customizer Customizers Customizer für Boiler Klasse (5/8) Property Change Registrierung Prepariere die Registrierungsmethode für die eigenen Property Changes public void addPropertyChangeListener( PropertyChangeListener l) { // defer to the support object support.addPropertyChangeListener(l); } public void removePropertyChangeListener( PropertyChangeListener l) { // defer to the support object support.removePropertyChangeListener(l); } getPrefferedSize() Im folgenden wird neben getPrefferedSize() auch noch die deprecated Version preferredSize() implementiert, da die BeanBox, wenigstens ältere Versionen davon, noch diese alte Methode aufruft. public Dimension preferredSize() { // defer to the getPreferredSize method return getPreferredSize(); } public Dimension getPreferredSize() { // we prefer a dimension of 480 by 80 return new Dimension(480, 80); } - 271 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 10. 10.2 Property Editoren und Customizer Customizers Customizer für Boiler Klasse (6/8) itemStateChanged() // handle an item state changed event public void itemStateChanged(ItemEvent evt) { // if a new selection is made in the // Running choice list... if (evt.getSource() == runningChoice && evt.getStateChange() == ItemEvent.SELECTED) // get the instance of the choice list Choice pp = (Choice)evt.getSource(); { // determine the newly selected item string String sel = pp.getSelectedItem(); // the desired boolean state of the Running property boolean state; if (sel.equals("YES")) { state = true; } else { state = false; } // create a new instance of Running Running newState = new Running(state); // set the Running property of the boiler bean theBoiler.setRunning(newState); } // fire a Running property change event support.firePropertyChange("Running", null, newState); } - 272 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 10. 10.2 Property Editoren und Customizer Customizers Customizer für Boiler Klasse (7/8) actionPerformed() actionPerformed() wird aufgerufen, wenn der Benutzer Enter gedrückt hat. Dies geschieht entweder in dem Rate Feld oder in dem Feld für die Operating Temperature. // handle an actionPerformed event public void actionPerformed(ActionEvent evt) { if (evt.getSource() == rateField) { // the event came from the rate field // get the text from the field String s = rateField.getText(); // get an integer representation of the new value int r = Integer.valueOf(s).intValue(); // set the Rate property theBoiler.setRate(r); // fire an unspecified property change event // the listener has to figure out what happened support.firePropertyChange("", null, null); } else { // the event came from the operating temp field // get the text from the field String s = tempField.getText(); // get a double representation of the new value double r = Double.valueOf(s).doubleValue(); // set the new operating temperature theBoiler.assignOperatingTemperature(r); } } - 273 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 10. 10.2 Property Editoren und Customizer Customizers Customizer für Boiler Klasse (8/8) propertyChange() Reagiert auf Änderung im Color Editor. } } public void propertyChange(PropertyChangeEvent evt) { Color c = (Color)colorEditor.getValue(); theBoiler.assignOnColor(c); Erweiterung der Manifest-Datei: Name: BeansBook/Simulator/BoilerCustomizer.class Java-Bean: False JAR Datei erstellen. Customizer kann in BeanBox durch Selektieren der Bean und Aufruf von „Edit/Customize“ in der Menüleiste aktiviert werden: - 274 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 11. 11.1 Reflection und Introspection nutzen Beispielanwendung BeanTool Reflection und Introspection nutzen Beispielanwendung BeanTool Benutzeroberfläche: • Ein leeres Fenster. • Hier hinein können Beans hinzugefügt werden. • Diese können miteinander verbunden werden. Einschränkungen: • Alle zu verwendenden Beans müssen sich in dem Verzeichnis beantool\jars befinden. • Nur Nachfahren der Klasse java.lang.Component werden behandelt. • Beans müssen visible sein und können keine Applets sein. • Beans sind – in diesem Beispiel noch – nicht konfugurierbar. Es gibt einen Dialog, um Beans auszuwählen: - 275 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 11. 11.1 Reflection und Introspection nutzen Beispielanwendung BeanTool Beispielanwendung BeanTool Und einen Dialog, um Beans zu verbinden: Der Quellcode: Klasse AddDialog Bean BeanInstance BeanTool ConnectDialog Jar JarClassLoader Beschreibung Stellt Dialogbox dar, mit deren Hilfe die Bean ausgesucht wird, die in den Layout Bereich eingefügt werden soll Namen der verfügbaren Beans Informationen über eine Bean-Instanz in dem Layout Bereich Hauptprogramm. Stellt Layout Bereich zur Verfügung Stellt Dialogbox dar, mit deren Hilfe zwei Beans in dem Layout Bereich miteinander verknüpft werden können Verarbeitet die Einträge einer Jar-Datei Lädt .class Einträge aus den Jar-Dateien - 276 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 11. 11.2 Reflection und Introspection nutzen Klasse BeanTool Klasse BeanTool (1/3) package beantool; import java.awt.*; import java.awt.event.*; import java.beans.*; import java.util.*; public class BeanTool extends Frame { private int x, y; // coordinates of mouse when clicked private Object bean1; // first bean to be connected // (event source) public static void main(String args[]) { Jar.process(); // process JAR files new BeanTool(); } public BeanTool() { super("Bean Tool"); addMouseListener(new MyMouseAdapter()); addWindowListener(new MyWindowAdapter()); setSize(400, 400); setVisible(true); } class MyMouseAdapter extends MouseAdapter { // catch mouse clicked only public void mouseClicked(MouseEvent me) { x = me.getX(); y = me.getY(); if(me.isAltDown()) { add(); } else if(me.isShiftDown()) { connect(); } repaint(); } } - 277 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 11. 11.2 Reflection und Introspection nutzen Klasse BeanTool Klasse BeanTool (2/3) class MyWindowAdapter extends WindowAdapter { public void windowClosing(WindowEvent we) { System.exit(0); } } public void add() { new AddDialog(this); // select and add a bean bean1 = null; } public void connect() { // connect two beans BeanInstance bi = selectBeanInstance(); if(bi != null) { if(bean1 == null) { bean1 = bi.getBean(); return; } Object bean2 = bi.getBean(); new ConnectDialog(this, "Title", bean1, bean2); } bean1 = null; } public BeanInstance selectBeanInstance() { // Determine which component is being selected by mouse Vector v = BeanInstance.getBeanInstances(); Enumeration e = v.elements(); while(e.hasMoreElements()) { BeanInstance bi = (BeanInstance)e.nextElement(); Component c = (Component)bi.getBean(); Point p = c.getLocation(); Dimension d = c.getSize(); Rectangle r = new Rectangle(p.x, p.y - 10, d.width - 1, 10); if(r.contains(x, y)) { return bi; } } return null; } - 278 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 11. 11.2 Reflection und Introspection nutzen Klasse BeanTool Klasse BeanTool (3/3) public void doLayout() { // Layout all of the components Vector v = BeanInstance.getBeanInstances(); Enumeration e = v.elements(); while(e.hasMoreElements()) { BeanInstance bi = (BeanInstance)e.nextElement(); Component c = (Component)bi.getBean(); Dimension d = c.getPreferredSize(); c.setBounds(bi.getX(), bi.getY(), d.width, d.height); c.doLayout(); } } public void insertBean(String beanName) { // Create a new instance of the Bean // named beanName; add to static Vector of BeanInstance BeanInstance bi = new BeanInstance(this, beanName, x, y); } } // Layout all components doLayout(); - 279 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 11. 11.3 Reflection und Introspection nutzen Klasse Jar Klasse Jar (1/4) Für jede .class Datei in der JAR Datei, die eine Bean ist, wird eine Bean Instanz erzeugt. package beantool; import java.io.*; import java.util.*; import java.util.zip.*; public class Jar { private static Hashtable data = new Hashtable(); private String filename; public static void putData(String clsName, byte[] buffer) { data.put(clsName, buffer); } public static Object getData(String clsName) { return data.get(clsName); } public static void process() { try { char c = File.separatorChar; File dir = new File("beantool" + c + "jars"); String entries[] = dir.list(); for(int i = 0; i < entries.length; i++) { if(entries[i].endsWith(".jar")) { new Jar("beantool" + c + "jars" + c + entries[i]); } } } catch(Exception ex) { ex.printStackTrace(); } } - 280 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 11. 11.3 Reflection und Introspection nutzen Klasse Jar Klasse Jar (2/4) public Jar(String filename) { // Read and process the .class entries in the JAR file this.filename = filename; try { FileInputStream fis = new FileInputStream(filename); ZipInputStream zis = new ZipInputStream(fis); ZipEntry ze = null; while((ze = zis.getNextEntry()) != null) { String name = ze.getName(); if(name.equals("META-INF/MANIFEST.MF")) { processManifestFile(zis); } else if(name.endsWith(".class")) { processClassFile(name, zis); } } zis.close(); } catch(Exception ex) { ex.printStackTrace(); } } - 281 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 11. 11.3 Reflection und Introspection nutzen Klasse Jar Klasse Jar (3/4) private void processManifestFile(ZipInputStream zis) { try { // Create a BufferedReader for the zip input stream InputStreamReader isr = new InputStreamReader(zis); BufferedReader br = new BufferedReader(isr); // Read lines // and create String name = String line = while((line = from the manifest file Bean objects null; null; br.readLine()) != null) { // Process lines starting with .class if(line.startsWith("Name: ") && line.endsWith(".class")) { name = line.substring(line.indexOf(":") + 2); } else if(line.startsWith("Java-Bean: ")) { if(name != null) { // Determine class name String name2 = name.replace('/', '.'); int i = name2.indexOf(".class"); name2 = name2.substring(0, i); } } } // Create a Bean object for that class new Bean(name2); name = null; } catch(Exception ex) { ex.printStackTrace(); } } - 282 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 11. 11.3 Reflection und Introspection nutzen Klasse Jar Klasse Jar (4/4) private void processClassFile(String name1, ZipInputStream zis) { // Determine class name String name2 = name1.replace('/','.'); int i = name2.indexOf(".class"); if(i != -1) { name2 = name2.substring(0, i); } try { // Read bytecodes from the zip input stream ByteArrayOutputStream baos = new ByteArrayOutputStream(); for(;;) { byte block[] = new byte[1024]; int len = zis.read(block); if(len < 0) { break; } baos.write(block, 0, len); } byte buffer[] = baos.toByteArray(); // Save these bytecodes putData(name2, buffer); } catch(Exception ex) { ex.printStackTrace(); } } } - 283 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 11. 11.4 Reflection und Introspection nutzen Klasse JarClassLoader Klasse JarClassLoader (1/2) package beantool; public class JarClassLoader extends ClassLoader { public static JarClassLoader singleton = new JarClassLoader(); protected Class loadClass(String clsName, boolean resolve) throws ClassNotFoundException { // Check the System class loader Class cls; try { cls = super.findSystemClass(clsName); return cls; } catch(ClassNotFoundException ex) { } catch(NoClassDefFoundError err) { } // Check if this class has already // been loaded cls = findLoadedClass(clsName); if(cls != null) { return cls; } // Get the bytecodes for this class byte buffer[] = (byte[])Jar.getData(clsName); if(buffer == null) { throw new ClassNotFoundException(); } // Parse the data cls = defineClass(clsName, buffer, 0, buffer.length); if(cls == null) { throw new ClassFormatError(); } - 284 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 11. 11.4 Reflection und Introspection nutzen Klasse JarClassLoader Klasse JarClassLoader (2/2) // Resolve the class if necessary if(resolve) { resolveClass(cls); } } } // Return the class return cls; - 285 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 11. 11.5 Reflection und Introspection nutzen Klasse Bean Klasse Bean package beantool; import java.util.*; public class Bean { private static Vector beans = new Vector(); private String name; public static Vector getBeans() { return beans; } public Bean(String name) { this.name = name; beans.addElement(this); } } public String getName() { return name; } - 286 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 11. 11.6 Reflection und Introspection nutzen Klasse AddDialog Klasse AddDialog (1/2) package beantool; import java.awt.*; import java.awt.event.*; import java.util.*; public class AddDialog extends Dialog implements ItemListener { private BeanTool beanTool; private java.awt.List list; public AddDialog(BeanTool beanTool) { // Invoke superclass constructor super(beanTool, "Add Dialog", true); // Initialize beanTool this.beanTool = beanTool; // Create and initialize list // and add it to dialog box list = new java.awt.List(); initializeList(); list.addItemListener(this); add("Center", list); // Register to receive window events addWindowListener(new MyWindowAdapter()); } // Set size of dialog and make it visible setSize(200, 200); show(); - 287 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 11. 11.6 Reflection und Introspection nutzen Klasse AddDialog Klasse AddDialog (2/2) private void initializeList() { // Initialize list Vector beans = Bean.getBeans(); Enumeration e = beans.elements(); while(e.hasMoreElements()) { Bean bean = (Bean)e.nextElement(); list.add(bean.getName()); } } public void itemStateChanged(ItemEvent ie) { // Process list selection String beanName = list.getSelectedItem(); beanTool.insertBean(beanName); dispose(); } } class MyWindowAdapter extends WindowAdapter { public void windowClosing(WindowEvent we) { dispose(); } } - 288 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 11. 11.7 Reflection und Introspection nutzen Klasse BeanInstance Klasse BeanInstance (1/2) Ein BeanIstance Objekt für jede Bean im Layout Bereich. Referenz zur Komponenten und Koordinaten im Layout Bereich. package beantool; import java.awt.*; import java.beans.*; import java.util.*; public class BeanInstance { private static Vector beanInstances = new Vector(); private Object bean; private int x, y; public static Vector getBeanInstances() { return beanInstances; } public BeanInstance(BeanTool beanTool, String beanName, int x, int y) { // Save the location of the component this.x = x; this.y = y; // Instantiate the component named beanName try { bean = Beans.instantiate(JarClassLoader.singleton, beanName); } catch(Exception ex) { return; } // Ignore invisible components if(!Beans.isInstanceOf(bean, Component.class)) { return; } - 289 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 11. 11.7 Reflection und Introspection nutzen Klasse BeanInstance Klasse BeanInstance (2/2) // Add the component to beanInstances beanInstances.addElement(this); // Position and layout the component Component c = (Component)bean; c.setLocation(x, y); c.doLayout(); } // Add the component to the BeanTool frame beanTool.add(c); public Object getBean() { return bean; } public int getX() { return x; } } public int getY() { return y; } - 290 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 11. 11.8 Reflection und Introspection nutzen Klasse ConnectDialog Klasse ConnectDialog (1/3) package beantool; import java.awt.*; import java.awt.event.*; import java.beans.*; import java.lang.reflect.*; import java.util.*; public class ConnectDialog extends Dialog implements ItemListener { private Object bean1, bean2; private java.awt.List list; EventSetDescriptor esds[]; public ConnectDialog(Frame frame, String title, Object bean1, Object bean2) { // Invoke superclass constructor super(frame, "Connect Dialog", true); // Save bean1 and bean2 this.bean1 = bean1; this.bean2 = bean2; // Create and initialize list list = new java.awt.List(); initializeList(); list.addItemListener(this); add("Center", list); // Register to receive window events addWindowListener(new MyWindowAdapter()); // Set size of dialog and make it visible setSize(200, 200); show(); } - 291 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 11. 11.8 Reflection und Introspection nutzen Klasse ConnectDialog Klasse ConnectDialog (2/3) private void initializeList() { // Get the BeanInfo object for bean1 Class cls1 = bean1.getClass(); BeanInfo bi1; try { bi1 = Introspector.getBeanInfo(cls1); } catch(Exception ex) { return; } } // Get the EventSetDescriptor objects for // bean1 and add these to the list esds = bi1.getEventSetDescriptors(); for(int i = 0; i < esds.length; i++) { list.add(esds[i].getName()); } - 292 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 11. 11.8 Reflection und Introspection nutzen Klasse ConnectDialog Klasse ConnectDialog (3/3) public void itemStateChanged(ItemEvent ie) { // Obtain the EventSetDescriptor object int index = list.getSelectedIndex(); EventSetDescriptor esd = esds[index]; // Obtain the registration method Method method = esd.getAddListenerMethod(); // Invoke the registration method of // bean1 to register bean2 Object args[] = new Object[1]; args[0] = bean2; try { method.invoke(bean1, args); } catch(Exception ex) { ex.printStackTrace(); } } } // Dispose of this dialog box dispose(); class MyWindowAdapter extends WindowAdapter { public void windowClosing(WindowEvent we) { dispose(); } } - 293 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 11. 11.9 Reflection und Introspection nutzen Introspection Szenarien Introspection Szenarien Ereignisse der Bean feststellen Wie kann eine Anwendug dynamisch feststellen, welche Ereignisse eine Bean erzeugen kann? 1. Wir rufen getBeanInfo() der Klasse Introspector auf, um ein Objekt zu erhalten, das das BeanInfo Interface implementiert. Dies wird via Reflection im Flug von Introspector erzeugt (Namensdkonventionen sind eingehalten, keine eigene BeanInfo Klasse). 2. Über getEventSetDescriptor() von BeanInfo erhalten wir einArray von EventSetDescriptor Objekten. Jedes dieser Objekte enthält Metadaten zu einem Event Set. 3. Wir fragen nach dem Default Event der Bean. 4. Über dessen Index erhalten wir die Klasse (Typ Class) des Listener Interfaces dieser Klasse. 5. Über getListenerMethods() des Default Event Set Descriptors erhalten wir ein Array mit den Methoden dieser Schnittstelle (Method Objekte). 6. Erhalte die AddListener und RemoveListener Methoden für diese Schnittstelle. 7. Finde heraus, ob das Ereignis ein Unicast Ereignis ist. - 294 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 11. 11.9 Reflection und Introspection nutzen Introspection Szenarien Introspection Szenarien Ereignisse der Bean feststellen Application Introspector getBeanInfo(MyBean.class) BeanInfo getEventSetDesciptors EventSetDesciptor[ ] getDefaultEventIndex getListenerType getListenerMethods getAddListenerMethod getRemoveListenerMethod isUnicast • Es kann auf diesem Weg noch wesentlich mehr an Informationen gefunden werden. • Im Prinzip wird immer zuerst das BeanInfo Objekt gefunden oder erzeugt. • Dieses stellt dann die weiteren Informationen zur Verfügung. - 295 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 11. 11.9 Reflection und Introspection nutzen Introspection Szenarien Introspection Szenarien Property-Werte der Bean feststellen Wir stellen die Properties einer Bean fest und holen uns den Wert einer Property über deren Zugriffsmethode. 1. Wir beginnen wieder damit, das BeanInfo Objekt zu erhalten. 2. Wir fragen nach der Property Deskriptoren der Bean. 3. Wir erfragen die Default Property. 4. Über deren Index erhalten wir die Lesemethode dieser Eigenschaft als Objekt der Klasse Method. 5. Mit Hilfe der Methode invoke() der Klasse Method rufen wir die Zugriffsmethode auf und erhalten den Wert der Property. Diese Methode wird weitgehend von Entwicklungswerkzeugen eingesetzt. Application Introspector getBeanInfo(MyBean.class) BeanInfo getPropertyDesciptors PropertyDesciptor[ ] getDefaultPropertyIndex getReadMethod Method invoke - 296 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 11. 11.9 Reflection und Introspection nutzen Introspection Szenarien Bean Analyse Programm (1/3) import java.beans.*; import java.awt.*; import java.lang.reflect.*; public class BeanAnalyst { static public void main(String args[]) { try { // Instantiate a bean Object s = Beans.instantiate(null, args[0]); // Introspect on class and stop at direct parent class BeanInfo bi = Introspector.getBeanInfo(s.getClass(), s.getClass().getSuperclass()); // Discover the display name BeanDescriptor bdsc = bi.getBeanDescriptor(); System.out.println("Hi! Your display name is = " + bdsc.getDisplayName()); // Get the bean's properties PropertyDescriptor[] pd = bi.getPropertyDescriptors(); System.out.println("\n\nYour properties are: \n"); for (int i = 0; i < pd.length; i++) { System.out.println("property = " + pd[i].getName() + " (write method = " + pd[i].getWriteMethod().getName() + ", read method = " + pd[i].getReadMethod().getName() + ")"); } // Get the bean's methods MethodDescriptor[] md = bi.getMethodDescriptors(); System.out.println("\n\nYour methods are: \n"); for (int i = 0; i < md.length; i++) { System.out.println("" + md[i].getMethod() ); } - 297 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 11. 11.9 Reflection und Introspection nutzen Introspection Szenarien Bean Analyse Programm (2/3) // Get the bean's events EventSetDescriptor[] evsd = bi.getEventSetDescriptors(); Method[] evm = evsd[0].getListenerMethods(); System.out.println( "\n\nYou emit the following Events: \n"); for (int i = 0; i < evm.length; i++) { System.out.println("" + evm[i].getName() ); } // Get the bean's icon types System.out.println( "\n\nYou provide the following icon types: \n"); if (bi.getIcon(BeanInfo.ICON_COLOR_32x32) != null) System.out.println("32x32 color"); if (bi.getIcon(BeanInfo.ICON_COLOR_16x16) != null) System.out.println("16x16 color"); if (bi.getIcon(BeanInfo.ICON_MONO_32x32) != null) System.out.println("32x32 mono"); if (bi.getIcon(BeanInfo.ICON_MONO_16x16) != null) System.out.println("16x16 mono"); if (Beans.isDesignTime()) System.out.println( "\n\nYour bean is in design-time mode."); else System.out.println( "\n\nYour bean is in run-time mode."); if (Beans.isGuiAvailable()) System.out.println("A GUI is available to your bean."); else System.out.println( "A GUI is not available to your bean."); - 298 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 11. 11.9 Reflection und Introspection nutzen Introspection Szenarien Bean Analyse Programm (2/3) // Are you a persistent bean? Class myClass = s.getClass(); boolean serializable = false; boolean externalizable = false; while (!myClass.getName().equals("java.lang.Object")) { Class[] interfaces = myClass.getInterfaces(); for (int i = 0; i < interfaces.length; i++) { if (interfaces[i].getName().equals( "java.io.Serializable")) serializable = true; if (interfaces[i].getName().equals( "java.io.Externalizable")) externalizable = true; } } myClass = myClass.getSuperclass(); if (serializable) System.out.println( "You are a serializable persistent bean"); if (externalizable) System.out.println( "You are an externalizable persistent bean"); System.exit(0); } } catch (Exception e) { System.out.println("Exception: " + e); } } - 299 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999 11. 11.9 Reflection und Introspection nutzen Introspection Szenarien Regeln, um anpaßbare Beans zu erstellen • Halte die JavaBeans Namenskonventionen hundertprozentig ein. Damit müssen keine BeanInfo Metadaten über die StandardEigenschaften, -Methoden und -Ereignisse zur Verfügung gestellt werden. • Erstelle eine BeanInfo Klasse mit speziellen Meta-Informationen, z.B. über einen Icon und benutzerfreundliche Namen. • Stelle Meta-Daten zur Verfügung, wenn der Introspector zu viele und verwirrende Daten liefern würde, z.B. bei einer tiefen Vererbungshierarchie. • Verwende eine Manifest-Datei, um alle Klassen zur Design-Zeit zu beschreiben. Ggf. erstelle eine JAR-Datei für das Design. • Entwickle Beans, so daß sie angepaßt und mit Werkzeugen bearbeitet werden können. Die Erstellung von BeanInfo Klassen, Property Editoren, Customizers, Manifest-Dateien und JARs kostet zusätzliche Aufwand. - 300 © Prof. Dr. B. Dreher, FB Informatik FH Wiesbaden – University of Applied Sciences - Spez. Methoden der Softwaretechnik: Software-Komponenten, SS 1999