/* Copyright (C) 2006 Christian Schneider
*
* This file is part of Nomad.
*
* Nomad is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Nomad 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Nomad; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.sf.nmedit.jpatch.transform;
import java.util.Collection;
import net.sf.nmedit.jpatch.MoveOperation;
import net.sf.nmedit.jpatch.PConnection;
import net.sf.nmedit.jpatch.PConnectionManager;
import net.sf.nmedit.jpatch.PConnector;
import net.sf.nmedit.jpatch.PConnectorDescriptor;
import net.sf.nmedit.jpatch.PModule;
import net.sf.nmedit.jpatch.PModuleContainer;
import net.sf.nmedit.jpatch.PModuleDescriptor;
import net.sf.nmedit.jpatch.PParameter;
import net.sf.nmedit.jpatch.PParameterDescriptor;
import net.sf.nmedit.jpatch.PRuntimeException;
/**
* Defines how the the parameters and connectors of two modules are mapped to each other.
* @author Christian Schneider
*/
public class PTModuleMapping
{
private PModuleDescriptor a;
private PModuleDescriptor b;
private PParameterDescriptor[] parameters;
private PConnectorDescriptor[] connectors;
private double covering = -1;
/**
* Creates a new mapping of the two modules a and b.
* The specified parameter/connector arrays must contains the component pairs of both modules.
* The elements at the index (2*index) must be components of module a.
* The elements at the index (2*index+1) must be components of module b.
* Each two fields ((2*index), (2*index+1)) are two components mapped to each other.
*
* @param a the mapped module
* @param b the mapped module
* @param parameters the parameter mappings
* @param connectors the connector mappings
*/
public PTModuleMapping(PModuleDescriptor a, PModuleDescriptor b, PParameterDescriptor[] parameters,
PConnectorDescriptor[] connectors)
{
this.a = a;
this.b = b;
this.parameters = parameters;
this.connectors = connectors;
}
/**
* Computes the relation of the parameters/connectors which are mapped and all existing components.
*
* @param weightParameters weights the parameter relation
* @param weightInputs weights the input connector relation
* @param weightOutputs weights the outpput connector relation
* @return returns a value in the range [0..1]. 1 means that all connectors/parameters of module A
* are mapped to all connectors/parameters of module B, 0 means that no mapping is possible (this statement
* is only true if the argements are all set to 1.0d).
*/
public double computeCovering(double weightParameters, double weightInputs, double weightOutputs)
{
final double epsilon = 10e-30;
if (weightInputs<0||weightOutputs<0||weightParameters<0)
throw new IllegalArgumentException("invalid weight:<0");
// normalize
{
double totalWeight = weightParameters+weightInputs+weightOutputs;
weightParameters/=totalWeight;
weightInputs/=totalWeight;
weightOutputs/=totalWeight;
}
// compute
double covering = 0;
if (weightParameters>epsilon)
{
int available = Math.max(a.getParameterDescriptorCount(),
b.getParameterDescriptorCount());
double value = (available == 0) ? 0 : (getParameterCount() / (double) available);
covering+=value*weightParameters;
}
if (weightInputs+weightOutputs>epsilon)
{
int maxAvailableInputs;
int maxAvailableOutputs;
{
int availableInputsA = 0;
int availableInputsB = 0;
int availableOutputsA = 0;
int availableOutputsB = 0;
for (int i=a.getConnectorDescriptorCount()-1;i>=0;i--)
if (a.getConnectorDescriptor(i).isOutput())
availableOutputsA++;
else
availableInputsA++;
for (int i=b.getConnectorDescriptorCount()-1;i>=0;i--)
if (b.getConnectorDescriptor(i).isOutput())
availableOutputsB++;
else
availableInputsB++;
maxAvailableInputs = Math.max(availableInputsA, availableInputsB);
maxAvailableOutputs = Math.max(availableOutputsA, availableOutputsB);
}
int inputs = 0;
int outputs = 0;
for (int i=getConnectorCount()-1;i>=0;i--)
{
// getConnectorA(i).isOutput()<=>getConnectorB(i).isOutput()
if (getConnectorA(i).isOutput())
outputs++;
else
inputs ++;
}
if (maxAvailableInputs > 0)
covering+=weightInputs*(inputs/(double)maxAvailableInputs);
if (maxAvailableOutputs > 0)
covering+=weightOutputs*(outputs/(double)maxAvailableOutputs);
}
return covering;
}
public double getConnectorCovering()
{
return computeCovering(0,1,1);
}
public double getParameterCovering()
{
return computeCovering(1,0,0);
}
/**
* Computes the relation of the parameters/connectors which are mapped and all existing components.
*
* @return returns a value in the range [0..1]. 1 means that all connectors/parameters of module A
* are mapped to all connectors/parameters of module B, 0 means that no mapping is possible.
*/
public double getCovering()
{
if (covering<0)
covering = computeCovering(1,1,1);
return covering;
}
/**
* Returns the number of mapped parameters.
* @return the number of mapped parameters
*/
public int getParameterCount()
{
return parameters.length/2;
}
/**
* Returns the number of mapped connectors.
* @return the number of mapped connectors
*/
public int getConnectorCount()
{
return connectors.length/2;
}
/**
* Returns module A.
* @return module A
*/
public PModuleDescriptor getModuleA()
{
return a;
}
/**
* Returns module B.
* @return module B
*/
public PModuleDescriptor getModuleB()
{
return b;
}
/**
* Returns the parameter of the module A at the specified index.
* @return parameter of module A
*/
public PParameterDescriptor getParameterA(int index)
{
return parameters[(2*index)];
}
/**
* Returns the parameter of the module B at the specified index.
* @return parameter of module B
*/
public PParameterDescriptor getParameterB(int index)
{
return parameters[(2*index)+1];
}
/**
* Returns the connector of the module A at the specified index.
* @return connector of module A
*/
public PConnectorDescriptor getConnectorA(int index)
{
return connectors[(2*index)];
}
/**
* Returns the connector of the module B at the specified index.
* @return connector of module B
*/
public PConnectorDescriptor getConnectorB(int index)
{
return connectors[(2*index)+1];
}
/**
* Transforms the specified module.
*
* @param source the source module
* @throws PRuntimeException if the module has no parent (module container)
* or the neither module A nor module B describe the specified source module,
* thus if this mapping is not able to transform the source module
*/
public PModule transform(PModule source)
{
int offset1;
int offset2;
PModuleDescriptor destination;
if (a.equals(source.getDescriptor()))
{
offset1 = 0;
offset2 = 1;
destination = b;
}
else if (b.equals(source.getDescriptor()))
{
offset1 = 1;
offset2 = 0;
destination = a;
}
else throw new PRuntimeException("transformation failed");
PModuleContainer container = source.getParentComponent();
if (container == null)
throw new PRuntimeException("module has no parent");
PConnectionManager cm = container.getConnectionManager();
// create new module
PModule m1 = source;
PModule m2 = container.createModule(destination);
int m1index = m1.getComponentIndex();
m2.setScreenLocation(m1.getScreenX(), m1.getScreenY());
// assign parameters
for (int i=parameters.length-2;i>=0;i-=2)
{
PParameter p1 = m1.getParameter(parameters[i+offset1]);
PParameter p2 = m2.getParameter(parameters[i+offset2]);
p2.setFloatValue(p1.getFloatValue());
}
// remember connections
Collection<PConnection> connections = null;
if (cm != null)
{
connections = cm.connections(m1);
cm.removeAllConnections(connections);
}
// replace module
if (!container.remove(m1)) return null;
container.add(m1index, m2);
MoveOperation move = m2.getParentComponent().createMoveOperation();
move.setScreenOffset(0, 0);
move.add(m2);
move.move();
// recreate connections
if (connections != null)
{
for (PConnection connection: connections)
{
PConnectorDescriptor prevA;
PConnector fix;
if (connection.getModuleA() == m1)
{
prevA = connection.getDescriptorA();
fix = connection.getB();
}
else if (connection.getModuleB() == m1)
{
prevA = connection.getDescriptorB();
fix = connection.getA();
}
else
{
continue;
}
PConnectorDescriptor newA = getDestination(prevA);
if (newA != null)
{
PConnector newConnector = m2.getConnector(newA);
if (newConnector == null)
throw new PRuntimeException("connector not found: "+newA);
cm.add(fix, newConnector);
}
}
}
return m2;
}
private PConnectorDescriptor getDestination(PConnectorDescriptor d)
{
for (int i=getConnectorCount()-1;i>=0;i--)
{
PConnectorDescriptor a = getConnectorA(i);
PConnectorDescriptor b = getConnectorB(i);
if (a==d) return b;
else if (b==d) return a;
else if (a.equals(d)) return b;
else if (b.equals(d)) return a;
}
return null;
}
public PModuleDescriptor getTarget(PModuleDescriptor source)
{
if (source == a) return b;
else if (source == b) return a;
else if (source.equals(a)) return b;
else if (source.equals(b)) return a;
else return null;
}
public String toString()
{
return getClass().getName()+"[a="+a+",b="+b+"]";
}
}