Simplify the RocketComponentVisitor by removing the generic interfaces.
Added functionality to the RocketComponentVisitor by providing a templated return value. Modified the Configuration tab UI to disable recovery & staging tabs when they are not appropriate and adding some help text to the motor table when there are no motor mounts defined.
This commit is contained in:
parent
f334fa1ec1
commit
305ad36103
@ -57,7 +57,7 @@ RocketPanel.lbl.ViewType = View Type:
|
||||
|
||||
! BasicFrame
|
||||
BasicFrame.tab.Rocketdesign = Rocket design
|
||||
BasicFrame.tab.Flightconfig = Configurations
|
||||
BasicFrame.tab.Flightconfig = Motors & Configuration
|
||||
BasicFrame.tab.Flightsim = Flight simulations
|
||||
BasicFrame.title.Addnewcomp = Add new component
|
||||
BasicFrame.dlg.lbl1 = Design '
|
||||
@ -1866,6 +1866,7 @@ MotorConfigurationPanel.btn.removeMotor = Remove motor
|
||||
MotorConfigurationPanel.btn.selectMotor = Select motor
|
||||
MotorConfigurationPanel.btn.selectIgnition = Select ignition
|
||||
MotorConfigurationPanel.btn.resetIgnition = Reset ignition
|
||||
MotorConfigurationPanel.lbl.nomotors = No motor mounts defined. Select one or more tubes from the list on the left as motor mounts.
|
||||
|
||||
MotorConfigurationTableModel.table.ignition.default = Default ({0})
|
||||
RecoveryConfigurationPanel.table.deployment.default = Default ({0})
|
||||
|
@ -28,7 +28,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
public abstract class RocketComponent implements ChangeSource, Cloneable, Iterable<RocketComponent>, Visitable<RocketComponentVisitor, RocketComponent> {
|
||||
public abstract class RocketComponent implements ChangeSource, Cloneable, Iterable<RocketComponent> {
|
||||
private static final Logger log = LoggerFactory.getLogger(RocketComponent.class);
|
||||
|
||||
// Because of changes to Java 1.7.0-45's mechanism to construct DataFlavor objects (used in Drag and Drop)
|
||||
@ -1736,9 +1736,9 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
|
||||
}
|
||||
|
||||
///////////// Visitor pattern implementation
|
||||
@Override
|
||||
public void accept(RocketComponentVisitor visitor) {
|
||||
public <R> R accept(RocketComponentVisitor<R> visitor) {
|
||||
visitor.visit(this);
|
||||
return visitor.getResult();
|
||||
}
|
||||
|
||||
//////////// Helper methods for subclasses
|
||||
|
@ -1,5 +1,19 @@
|
||||
package net.sf.openrocket.rocketcomponent;
|
||||
|
||||
public interface RocketComponentVisitor extends Visitor<RocketComponentVisitor, RocketComponent> {
|
||||
public interface RocketComponentVisitor<R> {
|
||||
|
||||
/**
|
||||
* The callback method. This method is the 2nd leg of the double-dispatch, having been invoked from a
|
||||
* corresponding <code>accept</code>.
|
||||
*
|
||||
* @param visitable the instance of the Visitable (the target of what is being visiting)
|
||||
*/
|
||||
void visit(RocketComponent visitable);
|
||||
|
||||
/**
|
||||
* Return the final result
|
||||
* @return
|
||||
*/
|
||||
R getResult();
|
||||
|
||||
}
|
||||
|
@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Visitable.java
|
||||
*/
|
||||
package net.sf.openrocket.rocketcomponent;
|
||||
|
||||
/**
|
||||
* This interface describes a portion of the Visitor pattern, using generics to assure type-safety.
|
||||
* The elements of the concrete object hierarchy are only visitable by an associated hierarchy of visitors,
|
||||
* while these visitors are only able to visit the elements of that hierarchy.
|
||||
*
|
||||
* The key concept regarding the Visitor pattern is to realize that Java will only "discriminate" the type of an
|
||||
* object being called, not the type of an object being passed.
|
||||
*
|
||||
* In order for the type of two objects to be determinable to the JVM, each object must be the receiver of an
|
||||
* invocation. Here, when accept is called on a Visitable, the concrete type of the Visitable becomes "known" but the
|
||||
* concrete type of the argument is still unknown. <code>visit</code> is then called on the parameter object, passing
|
||||
* the Visitable back, which has type and identity. Flow of control has now been 'double-dispatched' such that the
|
||||
* type (and identity) of both objects are known.
|
||||
*
|
||||
* Specifically, this interface is to be implemented by every class in the RocketComponent hierarchy that
|
||||
* can be visited AND which are sufficiently specialized from their super class. If they only provide
|
||||
* constraints to their superclass (such as TubeCoupler), then the implementation of this interface at
|
||||
* the superclass level is sufficient.
|
||||
*
|
||||
* Admittedly, the syntax is a bit contorted here, as it is necessarily self-referential for type-safety.
|
||||
*
|
||||
* <V> The visitor type
|
||||
* <T> The visitable (the concrete class that implements this interface)
|
||||
*/
|
||||
public interface Visitable<V extends Visitor<V, T>, T extends Visitable<V, T>> {
|
||||
|
||||
/**
|
||||
* Any class in the hierarchy that allows itself to be visited will implement this method. The normal
|
||||
* behavior is that the visitor will invoke this method of a Visitable, passing itself. The Visitable
|
||||
* turns around calls the Visitor back. This idiom is also known as 'double-dispatching'.
|
||||
*
|
||||
* @param visitor the visitor that will be called back
|
||||
*/
|
||||
public void accept(V visitor);
|
||||
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Visitor.java
|
||||
*/
|
||||
package net.sf.openrocket.rocketcomponent;
|
||||
|
||||
/**
|
||||
* This interface describes a portion of the Visitor pattern, using generics to assure type-safety.
|
||||
* The elements of the concrete object hierarchy are only visitable by an associated hierarchy of visitors,
|
||||
* while these visitors are only able to visit the elements of that hierarchy.
|
||||
*
|
||||
* The key concept regarding the Visitor pattern is to realize that Java will only "discriminate" the type of an
|
||||
* object being called, not the type of an object being passed.
|
||||
*
|
||||
* In order for the type of two objects to be determinable to the JVM, each object must be the receiver of an
|
||||
* invocation. Here, when accept is called on a Visitable, the concrete type of the Visitable becomes "known" but the
|
||||
* concrete type of the argument is still unknown. <code>visit</code> is then called on the parameter object, passing
|
||||
* the Visitable back, which has type and identity. Flow of control has now been 'double-dispatched' such that the
|
||||
* type (and identity) of both objects are known.
|
||||
*
|
||||
* Specifically, this interface is to be implemented by every class in the RocketComponent hierarchy that
|
||||
* can be visited AND which are sufficiently specialized from their super class. If they only provide
|
||||
* constraints to their superclass (such as TubeCoupler), then the implementation of this interface at
|
||||
* the superclass level is sufficient.
|
||||
*
|
||||
* Admittedly, the syntax is a bit contorted here, as it is necessarily self-referential for type-safety.
|
||||
*
|
||||
* <V> The visitor type (the concrete class that implements this interface)
|
||||
* <T> The visitable
|
||||
*/
|
||||
public interface Visitor<V extends Visitor<V, T>, T extends Visitable<V, T>> {
|
||||
|
||||
/**
|
||||
* The callback method. This method is the 2nd leg of the double-dispatch, having been invoked from a
|
||||
* corresponding <code>accept</code>.
|
||||
*
|
||||
* @param visitable the instance of the Visitable (the target of what is being visiting)
|
||||
*/
|
||||
void visit(T visitable);
|
||||
}
|
@ -3,24 +3,24 @@ package net.sf.openrocket.rocketvisitors;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponentVisitor;
|
||||
|
||||
public abstract class BredthFirstRecusiveVisitor implements RocketComponentVisitor {
|
||||
public abstract class BredthFirstRecusiveVisitor<R> implements RocketComponentVisitor<R> {
|
||||
|
||||
@Override
|
||||
public final void visit(RocketComponent visitable) {
|
||||
|
||||
this.doAction(visitable);
|
||||
|
||||
for ( RocketComponent child: visitable.getChildren() ) {
|
||||
for (RocketComponent child : visitable.getChildren()) {
|
||||
this.doAction(child);
|
||||
}
|
||||
|
||||
for ( RocketComponent child: visitable.getChildren() ) {
|
||||
for (RocketComponent child : visitable.getChildren()) {
|
||||
this.visit(child);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
protected abstract void doAction( RocketComponent visitable );
|
||||
protected abstract void doAction(RocketComponent visitable);
|
||||
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
package net.sf.openrocket.rocketvisitors;
|
||||
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||
import net.sf.openrocket.rocketcomponent.FlightConfigurableComponent;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||
|
||||
public class CopyFlightConfigurationVisitor extends DepthFirstRecusiveVisitor {
|
||||
public class CopyFlightConfigurationVisitor extends DepthFirstRecusiveVisitor<Void> {
|
||||
|
||||
private final String oldConfigId;
|
||||
private final String newConfigId;
|
||||
@ -17,10 +17,14 @@ public class CopyFlightConfigurationVisitor extends DepthFirstRecusiveVisitor {
|
||||
@Override
|
||||
public void doAction(RocketComponent visitable) {
|
||||
|
||||
if ( visitable instanceof FlightConfigurableComponent ) {
|
||||
((FlightConfigurableComponent)visitable).cloneFlightConfiguration(oldConfigId, newConfigId);
|
||||
if (visitable instanceof FlightConfigurableComponent) {
|
||||
((FlightConfigurableComponent) visitable).cloneFlightConfiguration(oldConfigId, newConfigId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void getResult() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,19 +3,19 @@ package net.sf.openrocket.rocketvisitors;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponentVisitor;
|
||||
|
||||
public abstract class DepthFirstRecusiveVisitor implements RocketComponentVisitor {
|
||||
public abstract class DepthFirstRecusiveVisitor<R> implements RocketComponentVisitor<R> {
|
||||
|
||||
@Override
|
||||
public final void visit(RocketComponent visitable) {
|
||||
|
||||
this.doAction(visitable);
|
||||
|
||||
for ( RocketComponent child: visitable.getChildren() ) {
|
||||
for (RocketComponent child : visitable.getChildren()) {
|
||||
this.visit(child);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected abstract void doAction( RocketComponent visitable );
|
||||
protected abstract void doAction(RocketComponent visitable);
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,30 @@
|
||||
package net.sf.openrocket.rocketvisitors;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||
|
||||
public class ListComponents<T extends RocketComponent> extends DepthFirstRecusiveVisitor<List<T>> {
|
||||
|
||||
private final Class<T> componentClazz;
|
||||
protected List<T> components = new ArrayList<T>();
|
||||
|
||||
public ListComponents(Class<T> componentClazz) {
|
||||
super();
|
||||
this.componentClazz = componentClazz;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<T> getResult() {
|
||||
return components;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doAction(RocketComponent visitable) {
|
||||
if (componentClazz.isAssignableFrom(visitable.getClass())) {
|
||||
components.add((T) visitable);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package net.sf.openrocket.rocketvisitors;
|
||||
|
||||
import net.sf.openrocket.rocketcomponent.MotorMount;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||
|
||||
public class ListMotorMounts extends ListComponents<RocketComponent> {
|
||||
|
||||
public ListMotorMounts() {
|
||||
super(RocketComponent.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doAction(RocketComponent visitable) {
|
||||
if (visitable instanceof MotorMount && ((MotorMount) visitable).isMotorMount()) {
|
||||
components.add(visitable);
|
||||
}
|
||||
}
|
||||
}
|
@ -16,8 +16,11 @@ import net.sf.openrocket.gui.dialogs.flightconfiguration.RenameConfigDialog;
|
||||
import net.sf.openrocket.gui.main.BasicFrame;
|
||||
import net.sf.openrocket.l10n.Translator;
|
||||
import net.sf.openrocket.rocketcomponent.FlightConfigurableComponent;
|
||||
import net.sf.openrocket.rocketcomponent.RecoveryDevice;
|
||||
import net.sf.openrocket.rocketcomponent.Rocket;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||
import net.sf.openrocket.rocketvisitors.ListComponents;
|
||||
import net.sf.openrocket.rocketvisitors.ListMotorMounts;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
import net.sf.openrocket.util.StateChangeListener;
|
||||
|
||||
@ -39,6 +42,10 @@ public class FlightConfigurationPanel extends JPanel implements StateChangeListe
|
||||
private final RecoveryConfigurationPanel recoveryConfigurationPanel;
|
||||
private final SeparationConfigurationPanel separationConfigurationPanel;
|
||||
|
||||
private final static int MOTOR_TAB_INDEX = 0;
|
||||
private final static int RECOVERY_TAB_INDEX = 1;
|
||||
private final static int SEPARATION_TAB_INDEX = 2;
|
||||
|
||||
@Override
|
||||
public void stateChanged(EventObject e) {
|
||||
updateButtonState();
|
||||
@ -178,10 +185,38 @@ public class FlightConfigurationPanel extends JPanel implements StateChangeListe
|
||||
|
||||
private void updateButtonState() {
|
||||
String currentId = rocket.getDefaultConfiguration().getFlightConfigurationID();
|
||||
// Enable the remove/rename/copy buttons only when a configuration is selected.
|
||||
removeConfButton.setEnabled(currentId != null);
|
||||
renameConfButton.setEnabled(currentId != null);
|
||||
copyConfButton.setEnabled(currentId != null);
|
||||
|
||||
// Count the number of motor mounts
|
||||
int motorMountCount = rocket.accept(new ListMotorMounts()).size();
|
||||
|
||||
// Count the number of recovery devices
|
||||
int recoveryDeviceCount = rocket.accept(new ListComponents<RecoveryDevice>(RecoveryDevice.class)).size();
|
||||
|
||||
// Count the number of stages
|
||||
int stageCount = rocket.getStageCount();
|
||||
|
||||
// Enable the new configuration button only when a motor mount is defined.
|
||||
newConfButton.setEnabled(motorMountCount > 0);
|
||||
|
||||
// Only enable the recovery tab if there is a motor mount and there is a recovery device
|
||||
tabs.setEnabledAt(RECOVERY_TAB_INDEX, motorMountCount > 0 && recoveryDeviceCount > 0);
|
||||
|
||||
// If the selected tab was the recovery tab, and there is no longer any recovery devices,
|
||||
// switch to the motor tab.
|
||||
if( recoveryDeviceCount == 0 && tabs.getSelectedIndex() == RECOVERY_TAB_INDEX ) {
|
||||
tabs.setSelectedIndex(MOTOR_TAB_INDEX);
|
||||
}
|
||||
|
||||
tabs.setEnabledAt(SEPARATION_TAB_INDEX, motorMountCount > 0 && stageCount > 1);
|
||||
|
||||
if ( stageCount ==1 && tabs.getSelectedIndex() == SEPARATION_TAB_INDEX ) {
|
||||
tabs.setSelectedIndex(MOTOR_TAB_INDEX);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package net.sf.openrocket.gui.main.flightconfigpanel;
|
||||
|
||||
import java.awt.CardLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
@ -49,6 +50,10 @@ public class MotorConfigurationPanel extends FlightConfigurablePanel<MotorMount>
|
||||
|
||||
private final JButton selectMotorButton, removeMotorButton, selectIgnitionButton, resetIgnitionButton;
|
||||
|
||||
private final JPanel cards;
|
||||
private final static String HELP_LABEL = "help";
|
||||
private final static String TABLE_LABEL = "table";
|
||||
|
||||
private final MotorChooserDialog motorChooserDialog;
|
||||
protected FlightConfigurableTableModel<MotorMount> configurationTableModel;
|
||||
|
||||
@ -57,7 +62,6 @@ public class MotorConfigurationPanel extends FlightConfigurablePanel<MotorMount>
|
||||
|
||||
motorChooserDialog = new MotorChooserDialog(SwingUtilities.getWindowAncestor(flightConfigurationPanel));
|
||||
|
||||
|
||||
{
|
||||
//// Select motor mounts
|
||||
JPanel subpanel = new JPanel(new MigLayout(""));
|
||||
@ -75,8 +79,16 @@ public class MotorConfigurationPanel extends FlightConfigurablePanel<MotorMount>
|
||||
this.add(subpanel, "split, w 200lp, growy");
|
||||
}
|
||||
|
||||
cards = new JPanel(new CardLayout());
|
||||
this.add( cards );
|
||||
|
||||
JLabel helpText = new JLabel(trans.get("MotorConfigurationPanel.lbl.nomotors"));
|
||||
cards.add(helpText, HELP_LABEL );
|
||||
|
||||
JScrollPane scroll = new JScrollPane(table);
|
||||
this.add(scroll, "grow, wrap");
|
||||
cards.add(scroll, TABLE_LABEL );
|
||||
|
||||
this.add(cards, "grow, wrap");
|
||||
|
||||
//// Select motor
|
||||
selectMotorButton = new JButton(trans.get("MotorConfigurationPanel.btn.selectMotor"));
|
||||
@ -122,6 +134,14 @@ public class MotorConfigurationPanel extends FlightConfigurablePanel<MotorMount>
|
||||
|
||||
}
|
||||
|
||||
protected void showEmptyText() {
|
||||
((CardLayout)cards.getLayout()).show(cards, HELP_LABEL);
|
||||
}
|
||||
|
||||
protected void showContent() {
|
||||
((CardLayout)cards.getLayout()).show(cards, TABLE_LABEL);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JTable initializeTable() {
|
||||
//// Motor selection table.
|
||||
@ -165,12 +185,21 @@ public class MotorConfigurationPanel extends FlightConfigurablePanel<MotorMount>
|
||||
}
|
||||
|
||||
private void updateButtonState() {
|
||||
if( configurationTableModel.getColumnCount() > 1 ) {
|
||||
showContent();
|
||||
String currentID = rocket.getDefaultConfiguration().getFlightConfigurationID();
|
||||
MotorMount currentMount = getSelectedComponent();
|
||||
selectMotorButton.setEnabled(currentMount != null && currentID != null);
|
||||
removeMotorButton.setEnabled(currentMount != null && currentID != null);
|
||||
selectIgnitionButton.setEnabled(currentMount != null && currentID != null);
|
||||
resetIgnitionButton.setEnabled(currentMount != null && currentID != null);
|
||||
} else {
|
||||
showEmptyText();
|
||||
selectMotorButton.setEnabled(false);
|
||||
removeMotorButton.setEnabled(false);
|
||||
selectIgnitionButton.setEnabled(false);
|
||||
resetIgnitionButton.setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user