package fr.orsay.lri.varna.models.templates;
import java.awt.Point;
import java.awt.geom.Point2D;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.Stack;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import fr.orsay.lri.varna.exceptions.ExceptionEdgeEndpointAlreadyConnected;
import fr.orsay.lri.varna.exceptions.ExceptionFileFormatOrSyntax;
import fr.orsay.lri.varna.exceptions.ExceptionInvalidRNATemplate;
import fr.orsay.lri.varna.exceptions.ExceptionXMLGeneration;
import fr.orsay.lri.varna.exceptions.ExceptionXmlLoading;
import fr.orsay.lri.varna.models.rna.RNA;
import fr.orsay.lri.varna.models.templates.RNATemplate.RNATemplateElement.EdgeEndPoint;
import fr.orsay.lri.varna.models.treealign.Tree;
/**
* A model for RNA templates.
* A template is a way to display an RNA secondary structure.
*
* @author Raphael Champeimont
*/
public class RNATemplate {
/**
* The list of template elements.
*/
private Collection<RNATemplateElement> elements = new ArrayList<RNATemplateElement>();
/**
* Tells whether the template contains elements.
*/
public boolean isEmpty() {
return elements.isEmpty();
}
/**
* The first endpoint (in sequence order) of the template.
* If there are multiple connected components, the first elements of one
* connected component will be returned.
* If the template contains no elements, null is returned.
* If there is a cycle, an arbitrary endpoint will be returned
* (as it then does not make sense to define the first endpoint).
* Time: O(n)
*/
public RNATemplateElement getFirst() {
return getFirstEndPoint().getElement();
}
/**
* The first endpoint edge endpoint (in sequence order) of the template.
* If there are multiple connected components, the first elements of one
* connected component will be returned.
* If the template contains no elements, null is returned.
* If there is a cycle, an arbitrary endpoint will be returned
* (as it then does not make sense to define the first endpoint).
* Time: O(n)
*/
public EdgeEndPoint getFirstEndPoint() {
if (elements.isEmpty()) {
return null;
} else {
Set<EdgeEndPoint> knownEndPoints = new HashSet<EdgeEndPoint>();
EdgeEndPoint currentEndPoint = getAnyEndPoint();
while (true) {
if (knownEndPoints.contains(currentEndPoint)) {
// There is a cycle in the template, so we stop there
// to avoid looping infinitely.
return currentEndPoint;
}
knownEndPoints.add(currentEndPoint);
EdgeEndPoint previousEndPoint = currentEndPoint.getPreviousEndPoint();
if (previousEndPoint == null) {
return currentEndPoint;
} else {
currentEndPoint = previousEndPoint;
}
}
}
}
/**
* Return an arbitrary element of the template,
* null if empty.
* Time: O(1)
*/
public RNATemplateElement getAny() {
if (elements.isEmpty()) {
return null;
} else {
return elements.iterator().next();
}
}
/**
* Return an arbitrary endpoint of the template,
* null if empty.
* Time: O(1)
*/
public EdgeEndPoint getAnyEndPoint() {
if (isEmpty()) {
return null;
} else {
return getAny().getIn1EndPoint();
}
}
/**
* Variable containing "this", used by the internal class
* to access this object.
*/
private final RNATemplate template = this;
/**
* To get an iterator of this class, use rnaIterator().
* See rnaIterator() for documentation.
*/
private class RNAIterator implements Iterator<RNATemplateElement> {
private Iterator<EdgeEndPoint> iter = vertexIterator();
public boolean hasNext() {
return iter.hasNext();
}
public RNATemplateElement next() {
if (! hasNext()) {
throw (new NoSuchElementException());
}
EdgeEndPoint currentEndPoint = iter.next();
switch (currentEndPoint.getPosition()) {
// We skip "IN" endpoints, so that we don't return elements twice
case IN1:
case IN2:
// We get the corresponding "OUT" endpoint
currentEndPoint = iter.next();
break;
}
return currentEndPoint.getElement();
}
public void remove() {
throw (new UnsupportedOperationException());
}
}
/**
* Iterates over the elements of the template, in the sequence order.
* Helixes will be given twice.
* Only one connected component will be iterated on.
* Note that if there is a cycle, the iterator may return a infinite
* number of elements.
*/
public Iterator<RNATemplateElement> rnaIterator() {
return new RNAIterator();
}
/**
* Iterates over all elements (each endpoint is given only once)
* in an arbitrary order.
*/
public Iterator<RNATemplateElement> classicIterator() {
return elements.iterator();
}
private class VertexIterator implements Iterator<EdgeEndPoint> {
private EdgeEndPoint endpoint = getFirstEndPoint();
public boolean hasNext() {
return (endpoint != null);
}
public EdgeEndPoint next() {
if (endpoint == null) {
throw (new NoSuchElementException());
}
EdgeEndPoint currentEndPoint = endpoint;
endpoint = currentEndPoint.getNextEndPoint();
return currentEndPoint;
}
public void remove() {
throw (new UnsupportedOperationException());
}
}
/**
* Iterates over the elements edge endpoints of the template,
* in the sequence order.
* Only one connected component will be iterated on.
* Note that if there is a cycle, the iterator may return a infinite
* number of elements.
*/
public Iterator<EdgeEndPoint> vertexIterator() {
return new VertexIterator();
}
private class MakeEdgeList {
List<EdgeEndPoint> list = new LinkedList<EdgeEndPoint>();
private void addEdgeIfNecessary(EdgeEndPoint endPoint) {
if (endPoint.isConnected()) {
list.add(endPoint);
}
}
public List<EdgeEndPoint> make() {
for (RNATemplateElement element: elements) {
if (element instanceof RNATemplateHelix) {
RNATemplateHelix helix = (RNATemplateHelix) element;
addEdgeIfNecessary(helix.getIn1());
addEdgeIfNecessary(helix.getIn2());
} else if (element instanceof RNATemplateUnpairedSequence) {
RNATemplateUnpairedSequence sequence = (RNATemplateUnpairedSequence) element;
addEdgeIfNecessary(sequence.getIn());
}
}
return list;
}
}
/**
* Return over all edges in an arbitrary order.
* For each edge, the first (5' side) endpoint will be given.
*/
public List<EdgeEndPoint> makeEdgeList() {
MakeEdgeList listMaker = new MakeEdgeList();
return listMaker.make();
}
private class RemovePseudoKnots {
/**
* The elements of the template as an array, in the order of the
* RNA sequence. Note that helixes will appear twice,
* and non-paired sequences don't appear.
*/
private ArrayList<RNATemplateHelix> helixesSeq;
/**
* For any i,
* j = helixesStruct[i] is the index in helixesSeq
* where the same helix also appears.
* It means we have for all i:
* helixesSeq[helixesStruct[i]] == helixesSeq[i]
*/
private ArrayList<Integer> helixesStruct;
/**
* The same as helixesStruct, but without the pseudoknots,
* ie. helixesStructWithoutPseudoKnots[i] may be -1
* even though helixesStruct[i] != -1 .
*/
private int[] helixesStructWithoutPseudoKnots;
private void initArrays() throws ExceptionInvalidRNATemplate {
helixesSeq = new ArrayList<RNATemplateHelix>();
helixesStruct = new ArrayList<Integer>();
Map<RNATemplateHelix,Integer> knownHelixes = new Hashtable<RNATemplateHelix,Integer>();
Iterator<RNATemplateElement> iter = rnaIterator();
while (iter.hasNext()) {
RNATemplateElement element = iter.next();
if (element instanceof RNATemplateHelix) {
helixesSeq.add((RNATemplateHelix) element);
int index = helixesSeq.size() - 1;
if (knownHelixes.containsKey(element)) {
// This is the second time we meet this helix.
int otherOccurenceIndex = knownHelixes.get(element);
helixesStruct.add(otherOccurenceIndex);
if (helixesStruct.get(otherOccurenceIndex) != -1) {
throw (new ExceptionInvalidRNATemplate("We met an helix 3 times. Is there a cycle?"));
}
// Now we know the partner of the other part of
// the helix.
helixesStruct.set(otherOccurenceIndex, index);
} else {
// This is the first time we meet this helix.
// Remember its index
knownHelixes.put((RNATemplateHelix) element, index);
// For the moment we don't know where the other part
// of the helix is, but this will be found later.
helixesStruct.add(-1);
}
}
}
}
/**
* Tells whether there is a pseudoknot.
* Adapted from RNAMLParser.isSelfCrossing()
*/
private boolean isSelfCrossing() {
Stack<Point> intervals = new Stack<Point>();
intervals.add(new Point(0, helixesStruct.size() - 1));
while (!intervals.empty()) {
Point p = intervals.pop();
if (p.x <= p.y) {
if (helixesStruct.get(p.x) == -1) {
intervals.push(new Point(p.x + 1, p.y));
} else {
int i = p.x;
int j = p.y;
int k = helixesStruct.get(i);
if ((k <= i) || (k > j)) {
return true;
} else {
intervals.push(new Point(i + 1, k - 1));
intervals.push(new Point(k + 1, j));
}
}
}
}
return false;
}
/**
* We compute helixesStructWithoutPseudoKnots
* from helixesStruct by replacing values by -1
* for the helixes we cut (the bases are become non-paired).
* We try to cut as few base pairs as possible.
*/
private void removePseudoKnotsAux() {
if (!isSelfCrossing()) {
helixesStructWithoutPseudoKnots = new int[helixesStruct.size()];
for (int i=0; i<helixesStructWithoutPseudoKnots.length; i++) {
helixesStructWithoutPseudoKnots[i] = helixesStruct.get(i);
}
} else {
// We need to get rid of pseudoknots
// This code was adapted from RNAMLParser.planarize()
int length = helixesStruct.size();
int[] result = new int[length];
for (int i = 0; i < result.length; i++) {
result[i] = -1;
}
short[][] tab = new short[length][length];
short[][] backtrack = new short[length][length];
// On the diagonal we have intervals containing only
// one endpoint. Therefore there can be no helix
// (because an helix consists of 2 elements).
for (int i = 0; i < result.length; i++) {
// tab[i][j] = 0;
backtrack[i][i] = -1;
}
for (int n = 1; n < length; n++) {
for (int i = 0; i < length - n; i++) {
int j = i + n;
tab[i][j] = tab[i + 1][j];
backtrack[i][j] = -1;
int k = helixesStruct.get(i);
assert k != -1;
if ((k <= j) && (i < k)) {
int tmp = helixesSeq.get(i).getLength();
if (i + 1 <= k - 1) {
tmp += tab[i + 1][k - 1];
}
if (k + 1 <= j) {
tmp += tab[k + 1][j];
}
if (tmp > tab[i][j]) {
tab[i][j] = (short) tmp;
backtrack[i][j] = (short) k;
}
}
}
}
// debug
//RNATemplateTests.printShortMatrix(tab);
Stack<Point> intervals = new Stack<Point>();
intervals.add(new Point(0, length - 1));
while (!intervals.empty()) {
Point p = intervals.pop();
if (p.x <= p.y) {
if (backtrack[p.x][p.y] == -1) {
result[p.x] = -1;
intervals.push(new Point(p.x + 1, p.y));
} else {
int i = p.x;
int j = p.y;
int k = backtrack[i][j];
result[i] = k;
result[k] = i;
intervals.push(new Point(i + 1, k - 1));
intervals.push(new Point(k + 1, j));
}
}
}
helixesStructWithoutPseudoKnots = result;
}
}
private Set<RNATemplateHelix> makeSet() {
Set<RNATemplateHelix> removedHelixes = new HashSet<RNATemplateHelix>();
for (int i=0; i<helixesStructWithoutPseudoKnots.length; i++) {
if (helixesStructWithoutPseudoKnots[i] < 0) {
removedHelixes.add(helixesSeq.get(i));
}
}
return removedHelixes;
}
public Set<RNATemplateHelix> removePseudoKnots() throws ExceptionInvalidRNATemplate {
initArrays();
removePseudoKnotsAux();
// debug
//printIntArrayList(helixesStruct);
//printIntArray(helixesStructWithoutPseudoKnots);
return makeSet();
}
}
private class ConvertToTree {
private Set<RNATemplateHelix> removedHelixes;
public ConvertToTree(Set<RNATemplateHelix> removedHelixes) {
this.removedHelixes = removedHelixes;
}
private Iterator<RNATemplateElement> iter = template.rnaIterator();
private Set<RNATemplateHelix> knownHelixes = new HashSet<RNATemplateHelix>();
public Tree<RNANodeValueTemplate> convert() throws ExceptionInvalidRNATemplate {
Tree<RNANodeValueTemplate> root = new Tree<RNANodeValueTemplate>();
// No value, this is a fake node because we need a root.
root.setValue(null);
makeChildren(root);
return root;
}
private void makeChildren(Tree<RNANodeValueTemplate> father) throws ExceptionInvalidRNATemplate {
List<Tree<RNANodeValueTemplate>> children = father.getChildren();
while (true) {
try {
RNATemplateElement element = iter.next();
if (element instanceof RNATemplateHelix) {
RNATemplateHelix helix = (RNATemplateHelix) element;
if (removedHelixes.contains(helix)) {
// Helix was removed
boolean firstPartOfHelix;
if (knownHelixes.contains(helix)) {
firstPartOfHelix = false;
} else {
knownHelixes.add(helix);
firstPartOfHelix = true;
}
int helixLength = helix.getLength();
// Maybe we could allow helixes of length 0?
// If we want to then this code can be changed in the future.
if (helixLength < 1) {
throw (new ExceptionInvalidRNATemplate("Helix length < 1"));
}
int firstPosition = firstPartOfHelix ? 0 : helixLength;
int afterLastPosition = firstPartOfHelix ? helixLength : 2*helixLength;
for (int i=firstPosition; i<afterLastPosition; i++) {
RNANodeValueTemplateBrokenBasePair value = new RNANodeValueTemplateBrokenBasePair();
value.setHelix(helix);
value.setPositionInHelix(i);
Tree<RNANodeValueTemplate> child = new Tree<RNANodeValueTemplate>();
child.setValue(value);
father.getChildren().add(child);
}
} else {
// We have an non-removed helix
if (knownHelixes.contains(helix)) {
if ((! (father.getValue() instanceof RNANodeValueTemplateBasePair))
|| ((RNANodeValueTemplateBasePair) father.getValue()).getHelix() != helix) {
// We have already met this helix, so unless it is our father,
// we have a pseudoknot (didn't we remove them???).
throw (new ExceptionInvalidRNATemplate("Unexpected helix. Looks like there still are pseudoknots even after we removed them so something is wrong about the template."));
} else {
// As we have found the father, we have finished our work
// with the children.
return;
}
} else {
knownHelixes.add(helix);
int helixLength = helix.getLength();
// Maybe we could allow helixes of length 0?
// If we want to then this code can be changed in the future.
if (helixLength < 1) {
throw (new ExceptionInvalidRNATemplate("Helix length < 1"));
}
Tree<RNANodeValueTemplate> lastChild = father;
for (int i=0; i<helixLength; i++) {
RNANodeValueTemplateBasePair value = new RNANodeValueTemplateBasePair();
value.setHelix(helix);
value.setPositionInHelix(i);
Tree<RNANodeValueTemplate> child = new Tree<RNANodeValueTemplate>();
child.setValue(value);
lastChild.getChildren().add(child);
lastChild = child;
}
// Now we put what follows as children of lastChild
makeChildren(lastChild);
}
}
} else if (element instanceof RNATemplateUnpairedSequence) {
RNATemplateUnpairedSequence sequence = (RNATemplateUnpairedSequence) element;
int seqLength = sequence.getLength();
// Maybe we could allow sequences of length 0?
// If we want to then this code can be changed in the future.
if (seqLength < 1) {
throw (new ExceptionInvalidRNATemplate("Non-paired sequence length < 1"));
}
RNANodeValueTemplateSequence value = new RNANodeValueTemplateSequence();
value.setSequence(sequence);
Tree<RNANodeValueTemplate> child = new Tree<RNANodeValueTemplate>();
child.setValue(value);
children.add(child);
} else {
throw (new ExceptionInvalidRNATemplate("We have an endpoint which is neither an helix nor a sequence. What is that?"));
}
} catch (NoSuchElementException e) {
// We are at the end of elements so if everything is ok
// the father must be the root.
if (father.getValue() == null) {
return; // Work finished.
} else {
throw (new ExceptionInvalidRNATemplate("Unexpected end of template endpoint list."));
}
}
}
}
}
/**
* Tells whether the connected component to which endPoint belongs to
* is cyclic.
*/
public boolean connectedComponentIsCyclic(EdgeEndPoint endPoint) {
Set<EdgeEndPoint> knownEndPoints = new HashSet<EdgeEndPoint>();
EdgeEndPoint currentEndPoint = endPoint;
while (true) {
if (knownEndPoints.contains(currentEndPoint)) {
return true;
}
knownEndPoints.add(currentEndPoint);
EdgeEndPoint previousEndPoint = currentEndPoint.getPreviousEndPoint();
if (previousEndPoint == null) {
return false;
} else {
currentEndPoint = previousEndPoint;
}
}
}
/**
* Tells whether the template elements are all connected, ie. if the
* graph (edge endpoints being vertices) is connected.
*/
public boolean isConnected() {
if (isEmpty()) {
return true;
}
// Count all vertices
int n = 0;
for (RNATemplateElement element: elements) {
if (element instanceof RNATemplateHelix) {
n += 4;
} else if (element instanceof RNATemplateUnpairedSequence) {
n += 2;
}
}
// Now try reaching all vertices
Set<EdgeEndPoint> knownEndPoints = new HashSet<EdgeEndPoint>();
EdgeEndPoint currentEndPoint = getFirstEndPoint();
while (true) {
if (knownEndPoints.contains(currentEndPoint)) {
// We are back to our original endpoint, so stop
break;
}
knownEndPoints.add(currentEndPoint);
EdgeEndPoint nextEndPoint = currentEndPoint.getNextEndPoint();
if (nextEndPoint == null) {
break;
} else {
currentEndPoint = nextEndPoint;
}
}
// The graph is connected iff the number of reached vertices
// is equal to the total number of vertices.
return (knownEndPoints.size() == n);
}
/**
* Checks whether this template is a valid RNA template, ie.
* it is non-empty, it does not contain a cycle and all elements are in one
* connected component.
*/
public void checkIsValidTemplate() throws ExceptionInvalidRNATemplate {
if (isEmpty()) {
throw (new ExceptionInvalidRNATemplate("The template is empty."));
}
if (! isConnected()) {
throw (new ExceptionInvalidRNATemplate("The template is a non-connected graph."));
}
// Now we know the graph is connected.
if (connectedComponentIsCyclic(getAnyEndPoint())) {
throw (new ExceptionInvalidRNATemplate("The template is cyclic."));
}
}
/**
* Make a tree of the template. For this, we will remove pseudoknots,
* taking care to remove as few base pair links as possible.
* Requires the template to be valid and will check the validity
* (will call checkIsValidTemplate()).
* Calling this method will automatically call computeIn1Is().
*/
public Tree<RNANodeValueTemplate> toTree() throws ExceptionInvalidRNATemplate {
// Compute the helix in1Is fields.
// We also rely on computeIn1Is() for checking the template validity.
computeIn1Is();
// Remove pseudoknots
RemovePseudoKnots pseudoKnotKiller = new RemovePseudoKnots();
Set<RNATemplateHelix> removedHelixes = pseudoKnotKiller.removePseudoKnots();
// Convert to tree
ConvertToTree converter = new ConvertToTree(removedHelixes);
return converter.convert();
}
/**
* Generate an RNA sequence that exactly matches the template.
* Requires the template to be valid and will check the validity
* (will call checkIsValidTemplate()).
*/
public RNA toRNA() throws ExceptionInvalidRNATemplate {
// First, we check this is a valid template
checkIsValidTemplate();
ArrayList<Integer> str = new ArrayList<Integer>();
Map<RNATemplateHelix, ArrayList<Integer>> helixes = new HashMap<RNATemplateHelix, ArrayList<Integer>>();
Iterator<RNATemplateElement> iter = rnaIterator();
while (iter.hasNext()) {
RNATemplateElement element = iter.next();
if (element instanceof RNATemplateHelix) {
RNATemplateHelix helix = (RNATemplateHelix) element;
int n = helix.getLength();
if (helixes.containsKey(helix)) {
int firstBase = str.size();
ArrayList<Integer> helixMembers = helixes.get(helix);
for (int i = 0; i < n; i++) {
int indexOfAssociatedBase = helixMembers.get(n-1-i);
str.set(indexOfAssociatedBase, firstBase + i);
str.add(indexOfAssociatedBase);
}
} else {
int firstBase = str.size();
ArrayList<Integer> helixMembers = new ArrayList<Integer>();
for (int i = 0; i < n; i++) {
// We don't known yet where the associated base is
str.add(-1);
helixMembers.add(firstBase + i);
}
helixes.put(helix, helixMembers);
}
} else if (element instanceof RNATemplateUnpairedSequence) {
RNATemplateUnpairedSequence sequence = (RNATemplateUnpairedSequence) element;
int n = sequence.getLength();
for (int i=0; i<n; i++) {
str.add(-1);
}
} else {
throw (new ExceptionInvalidRNATemplate("We have an endpoint which is neither an helix nor a sequence. What is that?"));
}
}
int[] strAsArray = RNATemplateAlign.intArrayFromList(str);
String[] seqAsArray = new String[strAsArray.length];
Arrays.fill(seqAsArray, " ");
RNA rna = new RNA();
try {
rna.setRNA(seqAsArray, strAsArray);
} catch (ExceptionFileFormatOrSyntax e) {
throw (new RuntimeException("Bug in toRNA(): setRNA() threw an ExceptionFileFormatOrSyntax exception."));
}
return rna;
}
private class ConvertToXml {
private Map<RNATemplateElement, String> elementNames = new HashMap<RNATemplateElement, String>();
private Element connectionsXmlElement;
private Document document;
private void addConnectionIfNecessary(EdgeEndPoint endPoint) {
if (endPoint != null && endPoint.isConnected()) {
RNATemplateElement e1 = endPoint.getElement();
EdgeEndPointPosition p1 = endPoint.getPosition();
RNATemplateElement e2 = endPoint.getOtherElement();
EdgeEndPointPosition p2 = endPoint.getOtherEndPoint().getPosition();
Element xmlElement = document.createElement("edge");
{
Element fromXmlElement = document.createElement("from");
fromXmlElement.setAttribute("endpoint", elementNames.get(e1));
fromXmlElement.setAttribute("position", p1.toString());
xmlElement.appendChild(fromXmlElement);
}
{
Element toXmlElement = document.createElement("to");
toXmlElement.setAttribute("endpoint", elementNames.get(e2));
toXmlElement.setAttribute("position", p2.toString());
xmlElement.appendChild(toXmlElement);
}
connectionsXmlElement.appendChild(xmlElement);
}
}
public Document toXMLDocument() throws ExceptionXMLGeneration, ExceptionInvalidRNATemplate {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
document = builder.newDocument();
Element root = document.createElement("RNATemplate");
document.appendChild(root);
Element elementsXmlElement = document.createElement("elements");
root.appendChild(elementsXmlElement);
connectionsXmlElement = document.createElement("edges");
root.appendChild(connectionsXmlElement);
// First pass, we create a mapping between java references and names (strings)
{
int nextHelix = 1;
int nextNonPairedSequence = 1;
for (RNATemplateElement templateElement: elements) {
if (templateElement instanceof RNATemplateHelix) {
RNATemplateHelix helix = (RNATemplateHelix) templateElement;
if (! elementNames.containsKey(helix)) {
elementNames.put(helix, "H ID " + nextHelix);
nextHelix++;
}
} else if (templateElement instanceof RNATemplateUnpairedSequence) {
RNATemplateUnpairedSequence sequence = (RNATemplateUnpairedSequence) templateElement;
if (! elementNames.containsKey(sequence)) {
elementNames.put(sequence, "S ID " + nextNonPairedSequence);
nextNonPairedSequence++;
}
} else {
throw (new ExceptionInvalidRNATemplate("We have an endpoint which is neither an helix nor a sequence. What is that?"));
}
}
}
// Now we generate the XML document
for (RNATemplateElement templateElement: elements) {
String elementXmlName = elementNames.get(templateElement);
Element xmlElement;
if (templateElement instanceof RNATemplateHelix) {
RNATemplateHelix helix = (RNATemplateHelix) templateElement;
xmlElement = document.createElement("helix");
xmlElement.setAttribute("id", elementXmlName);
xmlElement.setAttribute("length", Integer.toString(helix.getLength()));
xmlElement.setAttribute("flipped", Boolean.toString(helix.isFlipped()));
if (helix.hasCaption()) {
xmlElement.setAttribute("caption", helix.getCaption());
}
{
Element startPositionXmlElement = document.createElement("startPosition");
startPositionXmlElement.setAttribute("x", Double.toString(helix.getStartPosition().x));
startPositionXmlElement.setAttribute("y", Double.toString(helix.getStartPosition().y));
xmlElement.appendChild(startPositionXmlElement);
}
{
Element endPositionXmlElement = document.createElement("endPosition");
endPositionXmlElement.setAttribute("x", Double.toString(helix.getEndPosition().x));
endPositionXmlElement.setAttribute("y", Double.toString(helix.getEndPosition().y));
xmlElement.appendChild(endPositionXmlElement);
}
addConnectionIfNecessary(helix.getOut1());
addConnectionIfNecessary(helix.getOut2());
} else if (templateElement instanceof RNATemplateUnpairedSequence) {
RNATemplateUnpairedSequence sequence = (RNATemplateUnpairedSequence) templateElement;
xmlElement = document.createElement("sequence");
xmlElement.setAttribute("id", elementXmlName);
xmlElement.setAttribute("length", Integer.toString(sequence.getLength()));
{
Element vertex5XmlElement = document.createElement("vertex5");
vertex5XmlElement.setAttribute("x", Double.toString(sequence.getVertex5().x));
vertex5XmlElement.setAttribute("y", Double.toString(sequence.getVertex5().y));
xmlElement.appendChild(vertex5XmlElement);
}
{
Element vertex3XmlElement = document.createElement("vertex3");
vertex3XmlElement.setAttribute("x", Double.toString(sequence.getVertex3().x));
vertex3XmlElement.setAttribute("y", Double.toString(sequence.getVertex3().y));
xmlElement.appendChild(vertex3XmlElement);
}
{
Element inTangentVectorXmlElement = document.createElement("inTangentVector");
inTangentVectorXmlElement.setAttribute("angle", Double.toString(sequence.getInTangentVectorAngle()));
inTangentVectorXmlElement.setAttribute("length", Double.toString(sequence.getInTangentVectorLength()));
xmlElement.appendChild(inTangentVectorXmlElement);
}
{
Element outTangentVectorXmlElement = document.createElement("outTangentVector");
outTangentVectorXmlElement.setAttribute("angle", Double.toString(sequence.getOutTangentVectorAngle()));
outTangentVectorXmlElement.setAttribute("length", Double.toString(sequence.getOutTangentVectorLength()));
xmlElement.appendChild(outTangentVectorXmlElement);
}
addConnectionIfNecessary(sequence.getOut());
} else {
throw (new ExceptionInvalidRNATemplate("We have an endpoint which is neither an helix nor a sequence. What is that?"));
}
elementsXmlElement.appendChild(xmlElement);
}
return document;
} catch (ParserConfigurationException e) {
throw (new ExceptionXMLGeneration("ParserConfigurationException: " + e.getMessage()));
}
}
}
public void toXMLFile(File file) throws ExceptionXMLGeneration, ExceptionInvalidRNATemplate {
try {
Document xmlDocument = toXMLDocument();
Source source = new DOMSource(xmlDocument);
Result result = new StreamResult(file);
Transformer transformer;
transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.transform(source, result);
} catch (TransformerConfigurationException e) {
throw (new ExceptionXMLGeneration("TransformerConfigurationException: " + e.getMessage()));
} catch (TransformerFactoryConfigurationError e) {
throw (new ExceptionXMLGeneration("TransformerFactoryConfigurationError: " + e.getMessage()));
} catch (TransformerException e) {
throw (new ExceptionXMLGeneration("TransformerException: " + e.getMessage()));
}
}
public Document toXMLDocument() throws ExceptionXMLGeneration, ExceptionInvalidRNATemplate {
ConvertToXml converter = new ConvertToXml();
return converter.toXMLDocument();
}
private class LoadFromXml {
private Document xmlDocument;
private Map<String, RNATemplateElement> elementNames = new HashMap<String, RNATemplateElement>();
public LoadFromXml(Document xmlDocument) {
this.xmlDocument = xmlDocument;
}
private Point2D.Double pointFromXml(Element xmlPoint) {
Point2D.Double point = new Point2D.Double();
point.x = Double.parseDouble(xmlPoint.getAttribute("x"));
point.y = Double.parseDouble(xmlPoint.getAttribute("y"));
return point;
}
private double vectorLengthFromXml(Element xmlVector) {
return Double.parseDouble(xmlVector.getAttribute("length"));
}
private double vectorAngleFromXml(Element xmlVector) {
return Double.parseDouble(xmlVector.getAttribute("angle"));
}
/**
* Takes an element of the form:
* <anything endpoint="an element id" position="a valid EdgeEndPointPosition"/>
* and returns the corresponding EdgeEndPoint object.
*/
private EdgeEndPoint endPointFromXml(Element xmlEdgeEndPoint) throws ExceptionXmlLoading {
String elementId = xmlEdgeEndPoint.getAttribute("endpoint");
if (elementId == null || elementId == "")
throw (new ExceptionXmlLoading ("Missing endpoint attribute on " + xmlEdgeEndPoint));
String positionOnElement = xmlEdgeEndPoint.getAttribute("position");
if (positionOnElement == null || positionOnElement == "")
throw (new ExceptionXmlLoading ("Missing position attribute on " + xmlEdgeEndPoint));
if (elementNames.containsKey(elementId)) {
RNATemplateElement templateElement = elementNames.get(elementId);
EdgeEndPointPosition relativePosition = EdgeEndPointPosition.valueOf(positionOnElement);
if (relativePosition == null)
throw (new ExceptionXmlLoading ("Could not compute relativePosition"));
return templateElement.getEndPointFromPosition(relativePosition);
} else {
throw (new ExceptionXmlLoading("Edge is connected on unkown element: " + elementId));
}
}
private String connectErrMsg(EdgeEndPoint v1, EdgeEndPoint v2, String reason) {
return "Error while connecting\n"
+ v1.toString() + " to\n"
+ v2.toString() + " because:\n"
+ reason;
}
private void connect(EdgeEndPoint v1, EdgeEndPoint v2) throws ExceptionXmlLoading {
if (v1 == null || v2 == null) {
throw (new ExceptionXmlLoading("Invalid edge: missing endpoint\n v1 = " + v1 + "\n v2 = " + v2));
}
if (v2.isConnected()) {
throw (new ExceptionXmlLoading(connectErrMsg(v1, v2, "Second vertex is already connected to " + v2.getOtherElement().toString())));
}
if (v1.isConnected()) {
throw (new ExceptionXmlLoading(connectErrMsg(v1, v2, "First vertex is already connected to " + v1.getOtherElement().toString())));
}
try {
v1.connectTo(v2);
} catch (ExceptionEdgeEndpointAlreadyConnected e) {
throw (new ExceptionXmlLoading("A vertex is on two edges at the same time: " + e.getMessage()));
} catch (ExceptionInvalidRNATemplate e) {
throw (new ExceptionXmlLoading("ExceptionInvalidRNATemplate: " + e.getMessage()));
}
}
public void load() throws ExceptionXmlLoading {
// First part: we load all elements from the XML tree
Element xmlElements = (Element) xmlDocument.getElementsByTagName("elements").item(0);
{
NodeList xmlElementsChildren = xmlElements.getChildNodes();
for (int i=0; i<xmlElementsChildren.getLength(); i++) {
Node xmlElementsChild = xmlElementsChildren.item(i);
if (xmlElementsChild instanceof Element) {
Element xmlTemplateElement = (Element) xmlElementsChild;
String tagName = xmlTemplateElement.getTagName();
if (tagName == "helix") {
RNATemplateHelix helix = new RNATemplateHelix(xmlTemplateElement.getAttribute("id"));
helix.setFlipped(Boolean.parseBoolean(xmlTemplateElement.getAttribute("flipped")));
helix.setLength(Integer.parseInt(xmlTemplateElement.getAttribute("length")));
if (xmlTemplateElement.hasAttribute("caption")) {
helix.setCaption(xmlTemplateElement.getAttribute("caption"));
}
elementNames.put(xmlTemplateElement.getAttribute("id"), helix);
NodeList xmlHelixChildren = xmlTemplateElement.getChildNodes();
for (int j=0; j<xmlHelixChildren.getLength(); j++) {
Node xmlHelixChild = xmlHelixChildren.item(j);
if (xmlHelixChild instanceof Element) {
Element xmlHelixChildElement = (Element) xmlHelixChild;
String helixChildTagName = xmlHelixChildElement.getTagName();
if (helixChildTagName == "startPosition") {
helix.setStartPosition(pointFromXml(xmlHelixChildElement));
} else if (helixChildTagName == "endPosition") {
helix.setEndPosition(pointFromXml(xmlHelixChildElement));
}
}
}
} else if (tagName == "sequence") {
RNATemplateUnpairedSequence sequence = new RNATemplateUnpairedSequence(xmlTemplateElement.getAttribute("id"));
sequence.setLength(Integer.parseInt(xmlTemplateElement.getAttribute("length")));
elementNames.put(xmlTemplateElement.getAttribute("id"), sequence);
NodeList xmlSequenceChildren = xmlTemplateElement.getChildNodes();
for (int j=0; j<xmlSequenceChildren.getLength(); j++) {
Node xmlSequenceChild = xmlSequenceChildren.item(j);
if (xmlSequenceChild instanceof Element) {
Element xmlSequenceChildElement = (Element) xmlSequenceChild;
String sequenceChildTagName = xmlSequenceChildElement.getTagName();
if (sequenceChildTagName == "inTangentVector") {
sequence.setInTangentVectorLength(vectorLengthFromXml(xmlSequenceChildElement));
sequence.setInTangentVectorAngle(vectorAngleFromXml(xmlSequenceChildElement));
} else if (sequenceChildTagName == "outTangentVector") {
sequence.setOutTangentVectorLength(vectorLengthFromXml(xmlSequenceChildElement));
sequence.setOutTangentVectorAngle(vectorAngleFromXml(xmlSequenceChildElement));
} else if (sequenceChildTagName == "vertex5") {
sequence.setVertex5(pointFromXml(xmlSequenceChildElement));
} else if (sequenceChildTagName == "vertex3") {
sequence.setVertex3(pointFromXml(xmlSequenceChildElement));
}
}
}
}
}
}
}
// Second part: We read the edges from the XML tree
Element xmlEdges = (Element) xmlDocument.getElementsByTagName("edges").item(0);
{
NodeList xmlEdgesChildren = xmlEdges.getChildNodes();
for (int i=0; i<xmlEdgesChildren.getLength(); i++) {
Node xmlEdgesChild = xmlEdgesChildren.item(i);
if (xmlEdgesChild instanceof Element) {
Element xmlTemplateEdge = (Element) xmlEdgesChild;
if (xmlTemplateEdge.getTagName() == "edge") {
EdgeEndPoint v1 = null, v2 = null;
NodeList xmlEdgeChildren = xmlTemplateEdge.getChildNodes();
for (int j=0; j<xmlEdgeChildren.getLength(); j++) {
Node xmlEdgeChild = xmlEdgeChildren.item(j);
if (xmlEdgeChild instanceof Element) {
Element xmlEdgeChildElement = (Element) xmlEdgeChild;
String edgeChildTagName = xmlEdgeChildElement.getTagName();
if (edgeChildTagName == "from") {
v1 = endPointFromXml(xmlEdgeChildElement);
} else if (edgeChildTagName == "to") {
v2 = endPointFromXml(xmlEdgeChildElement);
}
}
}
if (v1 == null)
throw (new ExceptionXmlLoading("Invalid edge: missing \"from\" declaration"));
if (v2 == null)
throw (new ExceptionXmlLoading("Invalid edge: missing \"to\" declaration"));
connect(v1, v2);
}
}
}
}
}
}
public static RNATemplate fromXMLFile(File file) throws ExceptionXmlLoading {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setIgnoringElementContentWhitespace(true);
DocumentBuilder builder = factory.newDocumentBuilder();
Document xmlDocument = builder.parse(file);
return fromXMLDocument(xmlDocument);
} catch (ParserConfigurationException e) {
throw (new ExceptionXmlLoading("ParserConfigurationException: " + e.getMessage()));
} catch (SAXException e) {
throw (new ExceptionXmlLoading("SAXException: " + e.getMessage()));
} catch (IOException e) {
throw (new ExceptionXmlLoading("IOException: " + e.getMessage()));
}
}
public static RNATemplate fromXMLDocument(Document xmlDocument) throws ExceptionXmlLoading {
RNATemplate template = new RNATemplate();
LoadFromXml loader = template.new LoadFromXml(xmlDocument);
loader.load();
return template;
}
/**
* For an helix, tells us whether IN1/OUT1 is the 5' strand
* (the first strand we meet if we follow the RNA sequence)
* or the 3' strand (the second we meet if we follow the RNA sequence).
*/
public enum In1Is {
IN1_IS_5PRIME, IN1_IS_3PRIME
}
/**
* For each helix, compute the in1Is field.
* If helices connections are changed, the value may become obsolete,
* so you need to call this method again before accessing the in1Is
* fields if you have modified connections in the template.
* Requires the template to be valid and will check the validity
* (will call checkIsValidTemplate()).
*/
public void computeIn1Is() throws ExceptionInvalidRNATemplate {
checkIsValidTemplate();
Iterator<EdgeEndPoint> iter = vertexIterator();
Set<RNATemplateHelix> knownHelices = new HashSet<RNATemplateHelix>();
while (iter.hasNext()) {
EdgeEndPoint endPoint = iter.next();
RNATemplateElement templateElement = endPoint.getElement();
if (templateElement instanceof RNATemplateHelix) {
RNATemplateHelix helix = (RNATemplateHelix) templateElement;
if (! knownHelices.contains(helix)) {
// first time we meet this helix
switch (endPoint.getPosition()) {
case IN1:
case OUT1:
helix.setIn1Is(In1Is.IN1_IS_5PRIME);
break;
case IN2:
case OUT2:
helix.setIn1Is(In1Is.IN1_IS_3PRIME);
break;
}
knownHelices.add(helix);
}
}
}
}
/**
* Remove the element from the template.
* The element is automatically disconnected from any other element.
* Returns true if and only if the element was present in the template,
* otherwise nothing was done.
*/
public boolean removeElement(RNATemplateElement element) throws ExceptionInvalidRNATemplate {
if (elements.contains(element)) {
element.disconnectFromAny();
elements.remove(element);
return true;
} else {
return false;
}
}
/**
* Position of an endpoint on an endpoint.
* Not all values make sense for any endpoint.
* For an helix, all four make sense, but for a non-paired
* sequence, only IN1 and OUT1 make sense.
*/
public enum EdgeEndPointPosition {
IN1, IN2, OUT1, OUT2;
}
private static int NEXT_ID = 1;
/**
* An endpoint of an RNA template,
* it can be an helix or a sequence of non-paired bases.
*
* You cannot create an object of this class directly,
* use RNATemplateHelix or RNATemplateUnpairedSequence instead.
*
* @author Raphael Champeimont
*/
public abstract class RNATemplateElement {
public int _id = NEXT_ID++;
public String getName()
{return "RNATemplate"+_id; }
/**
* This variable is just there so that "this" can be accessed by a name
* from the internal class EdgeEndPoint.
*/
private final RNATemplateElement element = this;
/**
* When the endpoint is created, it is added to the list of elements
* in this template. To remove it, call RNATemplate.removeElement().
*/
public RNATemplateElement() {
elements.add(this);
}
/**
* Disconnect this endpoint from any other elements it may be connected to.
*/
public abstract void disconnectFromAny();
/**
* Get the the IN endpoint in the case of a sequence
* and the IN1 endpoint in the case of an helix.
*/
public abstract EdgeEndPoint getIn1EndPoint();
/**
* Returns the template to which this endpoint belongs.
*/
public RNATemplate getParentTemplate() {
return template;
}
/**
* Provided endpoint is an endpoint of this endpoint, get the next
* endpoint, either on this same endpoint, or or the connected endpoint.
* Note that you should use the getNextEndPoint() method of the endpoint
* itself directly.
*/
protected abstract EdgeEndPoint getNextEndPoint(EdgeEndPoint endpoint);
/**
* Provided endpoint is an endpoint of this endpoint, get the previous
* endpoint, either on this same endpoint, or or the connected endpoint.
* Note that you should use the getPreviousEndPoint() method of the endpoint
* itself directly.
*/
protected abstract EdgeEndPoint getPreviousEndPoint(EdgeEndPoint endpoint);
/**
* An edge endpoint is where an edge can connect.
*/
public class EdgeEndPoint {
private EdgeEndPoint() {
}
/**
* Get the next endpoint. If this endpoint is an "in" endpoint,
* returns the corresponding "out" endpoint. If this endpoint
* is an "out" endpoint, return the connected endpoint if there is
* one, otherwise return null.
*/
public EdgeEndPoint getNextEndPoint() {
return element.getNextEndPoint(this);
}
/**
* Same as getNextEndPoint(), but with the previous endpoint.
*/
public EdgeEndPoint getPreviousEndPoint() {
return element.getPreviousEndPoint(this);
}
/**
* Get the position on the endpoint where this endpoint is.
*/
public EdgeEndPointPosition getPosition() {
return element.getPositionFromEndPoint(this);
}
private EdgeEndPoint otherEndPoint;
/**
* Returns the endpoint on which this edge endpoint is.
*/
public RNATemplateElement getElement() {
return element;
}
/**
* Returns the other endpoint of the edge.
* Will be null if there is no edge connecter to this endpoint.
*/
public EdgeEndPoint getOtherEndPoint() {
return otherEndPoint;
}
/**
* Returns the endpoint at the other endpoint of the edge.
* Will be null if there is no edge connecter to this endpoint.
*/
public RNATemplateElement getOtherElement() {
return (otherEndPoint != null) ? otherEndPoint.getElement() : null;
}
/**
* Disconnect this endpoint from the other, ie. delete the edge
* between them. Note that this will modify both endpoints, and that
* x.disconnect() is equivalent to x.getOtherEndPoint().disconnect().
* If this endpoint is not connected, does nothing.
*/
public void disconnect() {
if (otherEndPoint != null) {
otherEndPoint.otherEndPoint = null;
otherEndPoint = null;
}
}
/**
* Tells whether this endpoint is connected with an edge to
* an other endpoint.
*/
public boolean isConnected() {
return (otherEndPoint != null);
}
/**
* Create an edge between two edge endpoints.
* This is a symmetric operation and it will modify both endpoints.
* It means x.connectTo(y) is equivalent to y.connectTo(x).
* The edge endpoint must be free (ie. not yet connected).
* Also, elements connected together must belong to the same template.
*/
public void connectTo(EdgeEndPoint otherEndPoint) throws ExceptionEdgeEndpointAlreadyConnected, ExceptionInvalidRNATemplate {
if (this.otherEndPoint != null || otherEndPoint.otherEndPoint != null) {
throw (new ExceptionEdgeEndpointAlreadyConnected());
}
if (template != otherEndPoint.getElement().getParentTemplate()) {
throw (new ExceptionInvalidRNATemplate("Elements from different templates cannot be connected with each other."));
}
this.otherEndPoint = otherEndPoint;
otherEndPoint.otherEndPoint = this;
}
public String toString() {
return "Edge endpoint on element " + element.toString() + " at position " + getPosition().toString();
}
}
/**
* Get the EdgeEndPoint object corresponding to the the given
* position on this endpoint.
*/
public abstract EdgeEndPoint getEndPointFromPosition(EdgeEndPointPosition position);
/**
* The inverse of getEndPointFromPosition.
*/
public abstract EdgeEndPointPosition getPositionFromEndPoint(EdgeEndPoint endPoint);
/**
* Connect the endpoint at position positionHere of this endpoint
* to the otherEndPoint.
*/
public void connectTo(
EdgeEndPointPosition positionHere,
EdgeEndPoint otherEndPoint)
throws ExceptionEdgeEndpointAlreadyConnected, ExceptionInvalidRNATemplate {
EdgeEndPoint endPointHere = getEndPointFromPosition(positionHere);
endPointHere.connectTo(otherEndPoint);
}
/**
* Connect the endpoint at position positionHere of this endpoint
* to the endpoint of otherElement at position positionOnOtherElement.
* @throws ExceptionInvalidRNATemplate
* @throws ExceptionEdgeEndpointAlreadyConnected, ExceptionEdgeEndpointAlreadyConnected
*/
public void connectTo(
EdgeEndPointPosition positionHere,
RNATemplateElement otherElement,
EdgeEndPointPosition positionOnOtherElement)
throws ExceptionEdgeEndpointAlreadyConnected, ExceptionEdgeEndpointAlreadyConnected, ExceptionInvalidRNATemplate {
EdgeEndPoint otherEndPoint = otherElement.getEndPointFromPosition(positionOnOtherElement);
connectTo(positionHere, otherEndPoint);
}
}
/**
* An helix in an RNA template.
*
* @author Raphael Champeimont
*/
public class RNATemplateHelix extends RNATemplateElement {
/**
* Number of base pairs in the helix.
*/
private int length;
/**
* Position of the helix start point,
* ie. the middle in the line [x,y] where (x,y)
* x is the base at the IN1 edge endpoint and
* y is the base at the OUT2 edge endpoint.
*/
private Point2D.Double startPosition;
/**
* Position of the helix end point,
* ie. the middle in the line [x,y] where (x,y)
* x is the base at the OUT1 edge endpoint and
* y is the base at the IN2 edge endpoint.
*/
private Point2D.Double endPosition;
/**
* Tells whether the helix is flipped.
*/
private boolean flipped = false;
public boolean isFlipped() {
return flipped;
}
public void setFlipped(boolean flipped) {
this.flipped = flipped;
}
/**
* For an helix, tells us whether IN1/OUT1 is the 5' strand
* (the first strand we meet if we follow the RNA sequence)
* or the 3' strand (the second we meet if we follow the RNA sequence).
* This information cannot be known locally, we need the complete
* template to compute it, see RNATemplate.computeIn1Is().
*/
private In1Is in1Is = null;
public In1Is getIn1Is() {
return in1Is;
}
public void setIn1Is(In1Is in1Is) {
this.in1Is = in1Is;
}
/**
* A string displayed on the helix.
*/
private String caption = null;
public String getCaption() {
return caption;
}
public void setCaption(String caption) {
this.caption = caption;
}
public boolean hasCaption() {
return caption != null;
}
/**
* If we go through all bases of the RNA from first to last,
* we will pass twice through this helix. On time, we arrive
* from in1, and leave by out2, and the other time we arrive from
* in2 and leave by out2.
* Whether we go through in1/out1 or in2/out2 the first time
* is written in the in1Is field.
*/
private final EdgeEndPoint in1, out1, in2, out2;
private String _name;
public RNATemplateHelix(String name) {
in1 = new EdgeEndPoint();
out1 = new EdgeEndPoint();
in2 = new EdgeEndPoint();
out2 = new EdgeEndPoint();
_name = name;
}
public String toString() {
return "Helix @" + Integer.toHexString(hashCode()) + " len=" + length + " caption=" + caption;
}
public String getName()
{return ""+_name; }
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
public Point2D.Double getStartPosition() {
return startPosition;
}
public void setStartPosition(Point2D.Double startPosition) {
this.startPosition = startPosition;
}
public Point2D.Double getEndPosition() {
return endPosition;
}
public void setEndPosition(Point2D.Double endPosition) {
this.endPosition = endPosition;
}
public EdgeEndPoint getIn1() {
return in1;
}
public EdgeEndPoint getOut1() {
return out1;
}
public EdgeEndPoint getIn2() {
return in2;
}
public EdgeEndPoint getOut2() {
return out2;
}
public void disconnectFromAny() {
getIn1().disconnect();
getIn2().disconnect();
getOut1().disconnect();
getOut2().disconnect();
}
protected EdgeEndPoint getNextEndPoint(EdgeEndPoint endpoint) {
if (endpoint == in1) {
return out1;
} else if (endpoint == in2) {
return out2;
} else {
return endpoint.getOtherEndPoint();
}
}
protected EdgeEndPoint getPreviousEndPoint(EdgeEndPoint endpoint) {
if (endpoint == out1) {
return in1;
} else if (endpoint == out2) {
return in2;
} else {
return endpoint.getOtherEndPoint();
}
}
public EdgeEndPoint getIn1EndPoint() {
return in1;
}
public EdgeEndPoint getEndPointFromPosition(
EdgeEndPointPosition position) {
switch (position) {
case IN1:
return getIn1();
case IN2:
return getIn2();
case OUT1:
return getOut1();
case OUT2:
return getOut2();
default:
return null;
}
}
public EdgeEndPointPosition getPositionFromEndPoint(
EdgeEndPoint endPoint) {
if (endPoint == in1) {
return EdgeEndPointPosition.IN1;
} else if (endPoint == in2) {
return EdgeEndPointPosition.IN2;
} else if (endPoint == out1) {
return EdgeEndPointPosition.OUT1;
} else if (endPoint == out2) {
return EdgeEndPointPosition.OUT2;
} else {
return null;
}
}
}
/**
* A sequence of non-paired bases in an RNA template.
*
* @author Raphael Champeimont
*/
public class RNATemplateUnpairedSequence extends RNATemplateElement {
/**
* Number of (non-paired) bases.
*/
private int length;
private static final double defaultTangentVectorAngle = Math.PI / 2;
private static final double defaultTangentVectorLength = 100;
/**
* The sequence is drawn along a cubic Bezier curve.
* The curve can be defined by 2 vectors, one for the start of the line
* and the other for the end. They are the tangents to the line at
* the beginning and the end of the line.
* Each vector can be defined by its length and its absolute angle.
* The angles are given in radians.
*/
private double inTangentVectorAngle = defaultTangentVectorAngle,
inTangentVectorLength = defaultTangentVectorLength,
outTangentVectorAngle = defaultTangentVectorAngle,
outTangentVectorLength = defaultTangentVectorLength;
/**
* Position of the begginning (at the "in" endpoint) of the line.
* It is only useful when the sequence is not yet connected to an helix.
* (Otherwise we can deduce it from this helix position).
*/
private Point2D.Double vertex5;
/**
* Position of the end (at the "out" endpoint) of the line.
* It is only useful when the sequence is not yet connected to an helix.
* (Otherwise we can deduce it from this helix position).
*/
private Point2D.Double vertex3;
public Point2D.Double getVertex5() {
return vertex5;
}
public void setVertex5(Point2D.Double vertex5) {
this.vertex5 = vertex5;
}
public Point2D.Double getVertex3() {
return vertex3;
}
public void setVertex3(Point2D.Double vertex3) {
this.vertex3 = vertex3;
}
/**
* The helixes connected on both sides.
* They must be helixes because only helixes have absolute positions,
* and the positions of the starting and ending points of the sequence
* are those stored in the helixes.
*/
private final EdgeEndPoint in, out;
private String _name;
public RNATemplateUnpairedSequence(String name) {
in = new EdgeEndPoint();
out = new EdgeEndPoint();
_name = name;
}
public String toString() {
return "Sequence @" + Integer.toHexString(hashCode()) + " len=" + length;
}
public String getName()
{return ""+_name; }
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
public double getInTangentVectorAngle() {
return inTangentVectorAngle;
}
public void setInTangentVectorAngle(double inTangentVectorAngle) {
this.inTangentVectorAngle = inTangentVectorAngle;
}
public double getInTangentVectorLength() {
return inTangentVectorLength;
}
public void setInTangentVectorLength(double inTangentVectorLength) {
this.inTangentVectorLength = inTangentVectorLength;
}
public double getOutTangentVectorAngle() {
return outTangentVectorAngle;
}
public void setOutTangentVectorAngle(double outTangentVectorAngle) {
this.outTangentVectorAngle = outTangentVectorAngle;
}
public double getOutTangentVectorLength() {
return outTangentVectorLength;
}
public void setOutTangentVectorLength(double outTangentVectorLength) {
this.outTangentVectorLength = outTangentVectorLength;
}
public EdgeEndPoint getIn() {
return in;
}
public EdgeEndPoint getOut() {
return out;
}
public void disconnectFromAny() {
getIn().disconnect();
getOut().disconnect();
}
protected EdgeEndPoint getNextEndPoint(EdgeEndPoint endpoint) {
if (endpoint == in) {
return out;
} else {
return endpoint.getOtherEndPoint();
}
}
protected EdgeEndPoint getPreviousEndPoint(EdgeEndPoint endpoint) {
if (endpoint == out) {
return in;
} else {
return endpoint.getOtherEndPoint();
}
}
public EdgeEndPoint getIn1EndPoint() {
return in;
}
public EdgeEndPoint getEndPointFromPosition(
EdgeEndPointPosition position) {
switch (position) {
case IN1:
return getIn();
case OUT1:
return getOut();
default:
return null;
}
}
public EdgeEndPointPosition getPositionFromEndPoint(
EdgeEndPoint endPoint) {
if (endPoint == in) {
return EdgeEndPointPosition.IN1;
} else if (endPoint == out) {
return EdgeEndPointPosition.OUT1;
} else {
return null;
}
}
}
}