/* ========================
* JSynoptic : a free Synoptic editor
* ========================
*
* Project Info: http://jsynoptic.sourceforge.net/index.html
*
* This program is free software; you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Foundation;
* either version 2.1 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* (C) Copyright 2001-2005, by :
* Corporate:
* EADS Astrium SAS
* EADS CRC
* Individual:
* Claude Cazenave
*
* $Id: Switch.java,v 1.5 2009/01/15 12:29:08 ogor Exp $
*
* Changes
* -------
* 08 sept 2008 : Initial public release (RO);
*
*/
package jsynoptic.plugins.circuit.switches;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import javax.swing.undo.CompoundEdit;
import simtools.data.DataException;
import simtools.data.DataSource;
import simtools.data.DataSourcePool;
import simtools.data.UnsupportedOperation;
import simtools.diagram.gate.Connection;
import simtools.diagram.gate.Gate;
import simtools.shapes.AbstractShape;
import simtools.shapes.undo.PropertyChangeEdit;
import simtools.ui.JPropertiesPanel;
import jsynoptic.base.DataSourceConsumer;
import jsynoptic.plugins.circuit.Abstract2DCircuitComponent;
import jsynoptic.plugins.circuit.CircuitGate;
import jsynoptic.plugins.circuit.CircuitPlugin;
import jsynoptic.plugins.circuit.ColoredSignal;
import jsynoptic.plugins.circuit.Signal;
import jsynoptic.plugins.circuit.ui.SwitchShapePropertiesPanel;
/**
*
* A switch is a circuit component composed of several gates that can be connected, according
* to a switch position.
*
* @author zxpletran007
*
*/
public abstract class Switch extends Abstract2DCircuitComponent implements DataSourceConsumer{
private static final long serialVersionUID = -7002331688071008405L;
/**
* Arc and lines inside the switch component that represents the connections between switch gates
*/
protected transient List switchInnerConnections;
protected transient boolean dirtyPosition = false;
protected transient DataSource switchPositionDs;
protected transient long switchPositionDsIndex;
/**
* Switch default position
*/
protected int defaultPosition;
/**
* Switch current position
*/
protected int currentPosition;
/**
* @param ox
* @param oy
* @param diameter
* @param defaultPosition
*/
public Switch(int ox, int oy, int width, int height, int defaultPosition) {
super(ox, oy, width, height);
this.defaultPosition = defaultPosition;
this.currentPosition = defaultPosition;
this.switchInnerConnections = new ArrayList();
}
/* (non-Javadoc)
* @see jsynoptic.builtin.Abstract1DShape#drawHook(java.awt.Graphics2D, boolean)
*/
protected void drawHook(Graphics2D g, boolean shapeDrawn) {
if (!shapeDrawn) {
return;
}
Object oldRendering = g.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// Draw switch connections
for(int i=0;i< switchInnerConnections.size();i++){
((SwitchInnerConnection) switchInnerConnections.get(i)).draw(g);
}
// Draw switch circle
g.draw(getSwitchFrame());
// Restore graphic properties
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, oldRendering);
}
protected void fillHook(Graphics2D g) {
Color c = getFillColor();
if (c!=null) {
g.setColor(c);
g.fill(getSwitchFrame());
}
}
/**
* Create switch connections according to current switch position
*/
protected abstract void updateSwitchInnerConnections();
/**
* Get the switch frame
* @return
*/
protected abstract Shape getSwitchFrame();
/* (non-Javadoc)
* @see jsynoptic.builtin.SimpleShape#getDelegateShape()
*/
protected Shape getDelegateShape() {
return null;
}
/* (non-Javadoc)
* @see simtools.shapes.AbstractShape#translate(int, int)
*/
public void translate(int dx, int dy) {
super.translate(dx, dy);
for(int i=0;i< switchInnerConnections.size();i++){
((SwitchInnerConnection) switchInnerConnections.get(i)).translate(dx, dy);
}
}
/* (non-Javadoc)
* @see simtools.diagram.Resizable#resize(int, int)
*/
public void resize(int dx, int dy) {
super.resize(dx, dy);
for(int i=0;i< switchInnerConnections.size();i++){
((SwitchInnerConnection) switchInnerConnections.get(i)).resize(dx, dy);
}
}
/**
* Update switch according to current bounding box
*/
public void updateSwitch(){
for(int i=0;i< switchInnerConnections.size();i++){
((SwitchInnerConnection) switchInnerConnections.get(i)).updateConnector();
}
}
/* (non-Javadoc)
* @see simtools.data.EndNotificationListener#notificationEnd(java.lang.Object)
*/
public void notificationEnd(Object referer) {
if (dirtyPosition) {
dirty |= setSwitchPosition(getDynamicSwichPosition());
dirtyPosition = false;
}
// parent for colors & update
super.notificationEnd(referer);
}
/**
* @return the switch position from the current dynamic SVG data source.
* Return the default position if the current data source value is not a valid position
*/
protected int getDynamicSwichPosition() {
int res = defaultPosition;
if ((switchPositionDs != null)) {
Object o;
try {
o = getSourceValue();
if (o instanceof Number){
int newpos = ((Number)o).intValue();
if (isValidPosition(newpos)){
res = newpos;
}
}
} catch (DataException e) {
}
}
return res;
}
protected Object getSourceValue() throws DataException {
if (switchPositionDs != null) {
return switchPositionDs.getValue(switchPositionDsIndex);
}
return null;
}
/**
* Set switch position
* @param newPosition
* @return true if switch position has changed
*/
public boolean setSwitchPosition(int newPosition){
boolean res = false;
if ( isValidPosition(newPosition) && newPosition != currentPosition){
res = true;
currentPosition = newPosition;
// 1- Update switch inner connections
updateSwitchInnerConnections();
// 2- Propagate signal
// All output gates forward a null signal
Iterator it = getGates().iterator();
while (it.hasNext()){
CircuitGate gate = (CircuitGate)it.next();
if (gate!=null && !gate.isInput()){
gate.sendSignal(null,Signal.getNewSignalEventId());
}
}
// All input gates forward their signal to output gates
it = getGates().iterator();
while (it.hasNext()){
CircuitGate gate = (CircuitGate)it.next();
if (gate!=null && gate.isInput() && gate.hasSignal()){
signalHasBeenReceived(gate);
}
}
}
return res;
}
/**
* @return <b>(boolean)</b> A boolean. True if the given position is a valid switch position.
* Shall be overloaded in subclasses, regarding the kind of switches.
*/
protected abstract boolean isValidPosition(int position);
/* (non-Javadoc)
* @see simtools.data.DataSourceListener#DataSourceIndexRangeChanged(simtools.data.DataSource, long, long)
*/
public void DataSourceValueChanged(DataSource ds, long minIndex, long maxIndex) {
if (ds.equals(switchPositionDs)) {
if ((switchPositionDsIndex >= minIndex) && (switchPositionDsIndex <= maxIndex)){
dirtyPosition = true;
}
}
super.DataSourceValueChanged(ds, minIndex, maxIndex);
}
/* (non-Javadoc)
* @see simtools.data.DataSourceListener#DataSourceValueChanged(simtools.data.DataSource, long, long)
*/
public void DataSourceIndexRangeChanged(DataSource ds, long startIndex, long lastIndex) {
if (ds.equals(switchPositionDs)) {
switchPositionDsIndex = lastIndex;
dirtyPosition = true;
}
super.DataSourceIndexRangeChanged(ds, startIndex, lastIndex);
}
/* (non-Javadoc)
* @see simtools.data.DataSourceListener#DataSourceReplaced(simtools.data.DataSource, simtools.data.DataSource)
*/
public void DataSourceReplaced(DataSource oldData, DataSource newData) {
if(oldData==switchPositionDs){
switchPositionDs=newData;
if (switchPositionDs!=null) {
switchPositionDs.addListener(this);
switchPositionDs.addEndNotificationListener(this);
try {
switchPositionDsIndex = switchPositionDs.getLastIndex();
} catch (UnsupportedOperation uo1) {
switchPositionDsIndex = 0;
}
}
oldData.removeListener(this);
oldData.removeEndNotificationListener(this);
dirtyPosition = true;
}
super.DataSourceReplaced(oldData,newData);
}
/*
* (non-Javadoc)
*
* @see jsynoptic.base.DataSourceConsumer#addDataSource(simtools.data.DataSource)
*/
public boolean addDataSource(DataSource ds) {
setSwitchPositionDs(ds);
notifyChange();
return true;
}
/**
* Set switch position data source reference
* @param ds - the data source (can be null)
*/
protected void setSwitchPositionDs(DataSource ds){
if (switchPositionDs != null) {
// if there is already a source for this shape,
// remove it
switchPositionDs.removeListener(this);
switchPositionDs.removeEndNotificationListener(this);
}
switchPositionDs = ds;
if (ds instanceof DataSource) {
try {
switchPositionDsIndex = switchPositionDs.getLastIndex();
} catch (UnsupportedOperation e) {
switchPositionDsIndex = 0;
}
switchPositionDs.addListener(this);
switchPositionDs.addEndNotificationListener(this);
}
setSwitchPosition(getDynamicSwichPosition());
}
public int getCurrentPosition() {
return currentPosition;
}
/* (non-Javadoc)
* @see simtools.diagram.gate.GatedComponent#connectionAddedAt(simtools.diagram.gate.Gate, simtools.diagram.gate.Connection)
*/
public void connectionAddedAt(Gate gate, Connection connection) {
// If the other connection gate holds a signal, the given gate becomes an input of this component
Gate otherGate = null;
if (connection.getFirstEndGate() != null && !connection.getFirstEndGate().equals(gate)){
otherGate = connection.getFirstEndGate();
} else if (connection.getLastEndGate() != null && !connection.getLastEndGate().equals(gate)){
otherGate = connection.getLastEndGate();
}
if (otherGate != null
&& otherGate instanceof CircuitGate
&& ((CircuitGate)otherGate).hasSignal()
){
((CircuitGate)gate).setInput(true);
}
}
/* (non-Javadoc)
* @see simtools.diagram.gate.GatedComponent#connectionRemovedAt(simtools.diagram.gate.Gate, simtools.diagram.gate.Connection)
*/
public void connectionRemovedAt(Gate gate, Connection connection) {
}
/* (non-Javadoc)
* @see simtools.shapes.AbstractShape#subscribeToDataNotifications()
*/
public void processShapeRestoring(){
if (switchPositionDs!=null) {
switchPositionDs.addListener(this);
switchPositionDs.addEndNotificationListener(this);
try {
switchPositionDsIndex = switchPositionDs.getLastIndex();
} catch (UnsupportedOperation uo1) {
switchPositionDsIndex = 0;
}
}
super.processShapeRestoring();
}
/* (non-Javadoc)
* @see simtools.shapes.AbstractShape#unsubscribeToDataNotifications()
*/
public void processShapeRemoving(){
if (switchPositionDs != null) {
switchPositionDs.removeListener(this);
switchPositionDs.removeEndNotificationListener(this);
}
super.processShapeRemoving();
}
// Take care of serialisation. Special handling for datasources
private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException {
out.defaultWriteObject();
DataSourcePool.global.writeDataSource(out, switchPositionDs);
}
private void readObject(java.io.ObjectInputStream in) throws java.lang.ClassNotFoundException, java.io.IOException {
in.defaultReadObject();
switchPositionDs = DataSourcePool.global.readDataSource(in);
if (switchPositionDs!=null)
try {
switchPositionDs.addListener(this);
switchPositionDs.addEndNotificationListener(this);
switchPositionDsIndex= switchPositionDs.getLastIndex();
}
catch (UnsupportedOperation uo1) {
switchPositionDsIndex = 0;
}
currentPosition = getDynamicSwichPosition();
switchInnerConnections = new ArrayList();
}
/* (non-Javadoc)
* @see simtools.shapes.AbstractShape#cloneShape()
*/
protected AbstractShape cloneShape() {
Switch es = (Switch)super.cloneShape();
es.gateHolder = gateHolder.cloneGateHolder(es);
// Switch inner connections
es.switchInnerConnections = new ArrayList();
for(int i=0;i<switchInnerConnections.size();i++){
SwitchInnerConnection sc=(SwitchInnerConnection)switchInnerConnections.get(i);
try {
es.switchInnerConnections.add( es.cloneSwitchConnection(sc) );
} catch (InvalidSwitchConnection e) {
e.printStackTrace();
}
}
if(es.switchPositionDs!=null){
es.switchPositionDs.addEndNotificationListener(es);
es.switchPositionDs.addListener(es);
}
return es;
}
//***** GateComponent methods
/* (non-Javadoc)
* @see jsynoptic.plugins.circuit.CircuitComponent#gateSignalHasChanged(jsynoptic.plugins.circuit.Gate)
*/
public void signalHasBeenReceived(CircuitGate sourceGate) {
if (sourceGate != null){
// The source gate is an input
sourceGate.setInput(true);
CircuitGate targetGate = getTargetGate(sourceGate);
if (targetGate != null){
// The source gate is an output
targetGate.setInput(false);
// Forward signal to target gate
targetGate.sendSignal(sourceGate.getCurrentSignal(), sourceGate.getCurrentSignalEventId());
}
}
}
/**
* Get the gate on which the incoming signal shall be propagated, according to the source gate
* Return null if there is no target gate for this source gate.
* @param sourceGate
* @return <b>(Gate)</b> A Gate. The gate gate
*/
protected abstract CircuitGate getTargetGate(CircuitGate sourceGate);
protected abstract SwitchInnerConnection cloneSwitchConnection(SwitchInnerConnection sc) throws InvalidSwitchConnection;
/**
* Switch Inner Connection
* A switch is composed of 8 inner connectors:
* <br><br>
* C2 C3 C4<br>
* C1 [] C5<br>
* C8 C7 C6<br>
* <br><br>
* The inner connection is either a line or an arc
* <ul>
* <li>If the end connector index - start connector index = 4, a line is created
* <li>If the end connector index - start connector index = 2, an arc is created
* <li>Otherwise a exception is thrown
* </ul>
*
* @author zxpletran007
*/
public abstract class SwitchInnerConnection {
protected ColoredSignal connectionSignal = null;
protected abstract Shape getShape();
protected abstract void translate(int dx, int dy);
protected abstract void resize(int dx, int dy);
protected abstract void updateConnector();
public void draw(Graphics2D g){
Shape s = getShape();
if (s != null){
Color oldColor = g.getColor();
g.setColor(connectionSignal!= null? connectionSignal.getColor() : drawColor);
g.draw(s);
g.setColor(oldColor);
}
}
}
public static class InvalidSwitchConnection extends Exception{
public InvalidSwitchConnection(String arg0) {
super(arg0);
}
}
public static class SwitchShapePropertiesNames extends Abstract2DShapePropertiesNames {
private static transient String[] props = new String[] {
"SWITCH_POSITION_SOURCE"
};
public SwitchShapePropertiesNames() {
super();
for (int i = 0; i < props.length; i++) {
propertyNames.add(props[i]);
}
}
}
public String[] getPropertyNames() {
if (_propertyNames == null) {
_propertyNames = new SwitchShapePropertiesNames().getPropertyNames();
}
return _propertyNames;
}
public Object getPropertyValue(String name) {
Object res = super.getPropertyValue(name);
if (name.equalsIgnoreCase("SWITCH_POSITION_SOURCE")) {
res = switchPositionDs;
}
return res;
}
/*
* (non-Javadoc)
*
* @see simtools.shapes.AbstractShape#setProperty(java.lang.String,
* java.lang.Object)
*/
public void setPropertyValue(String name, Object value) {
super.setPropertyValue(name, value);
if (name.equalsIgnoreCase("SWITCH_POSITION_SOURCE")) {
setSwitchPositionDs( (DataSource)value);
updateSwitch();
}
}
/* (non-Javadoc)
* @see simtools.shapes.AbstractShape#getPanel(java.util.List)
*/
public JPropertiesPanel getPanel(List properties) {
if (properties == null){
return null;
}
if (properties.containsAll(Arrays.asList(new SwitchShapePropertiesNames().getPropertyNames()))){
return new SwitchShapePropertiesPanel(CircuitPlugin.resources.getString("Switch"));
} else {
return super.getPanel(properties);
}
}
/*
* (non-Javadoc)
*
* @see jsynoptic.base.DataSourceConsumer#canAddDataSource(simtools.data.DataSource)
*/
public boolean canAddDataSource(DataSource d) {
return true;
}
public boolean doAction(double x, double y, Object o, String action, CompoundEdit undoableEdit) {
boolean res;
if (action.equals(CircuitPlugin.resources.getString("setSource"))) {
// Save old params
CompoundEdit ce = new CompoundEdit();
HashMap oldValues = new HashMap();
oldValues.put("SWITCH_POSITION_SOURCE", switchPositionDs);
addDataSource((DataSource)o);
// Notify changes
Iterator it = oldValues.keySet().iterator();
while (it.hasNext()) {
String n = (String) it.next();
ce.addEdit(new PropertyChangeEdit(this, n, oldValues.get(n), getPropertyValue(n)));
}
ce.end();
if (undoableEdit != null) {
undoableEdit.addEdit(ce);
}
res = true;
} else {
res = super.doAction(x,y,o,action, undoableEdit);
}
return res;
}
public String[] getActions(double x, double y, Object o, int context) {
if (context == MOUSE_OVER_CONTEXT) {
return null;
}
if (context == MOUSE_OUT_CONTEXT) {
return null;
}
if (context == MOUSE_PRESSED_CONTEXT) {
return null;
}
ArrayList v = new ArrayList();
if ( (context == EDITOR_CONTEXT) && (o instanceof DataSource)){
v.add(CircuitPlugin.resources.getString("setSource"));
}
return (String[])v.toArray(new String[v.size()]);
}
}