[med-svn] [biojava4-live] 01/05: New upstream version 4.2.7+dfsg

Olivier Sallou osallou at debian.org
Mon May 15 12:33:11 UTC 2017


This is an automated email from the git hooks/post-receive script.

osallou pushed a commit to branch master
in repository biojava4-live.

commit 44affdce0fd70f93fe9f78f7444e91a2424ad7fe
Author: Olivier Sallou <osallou at debian.org>
Date:   Mon May 15 12:16:04 2017 +0000

    New upstream version 4.2.7+dfsg
---
 biojava-aa-prop/pom.xml                            |   6 +-
 biojava-alignment/pom.xml                          |   6 +-
 biojava-core/pom.xml                               |   2 +-
 biojava-genome/pom.xml                             |   6 +-
 .../nbio/genome/util/ChromosomeMappingTools.java   | 387 ++++++---------------
 .../org/biojava/nbio/genome/TestGenomeMapping.java | 380 ++++++++++++++------
 biojava-integrationtest/pom.xml                    |   4 +-
 .../nbio/structure/test/StructureToolsTest.java    |  51 ++-
 biojava-modfinder/pom.xml                          |   4 +-
 biojava-ontology/pom.xml                           |   2 +-
 biojava-phylo/pom.xml                              |   4 +-
 biojava-protein-disorder/pom.xml                   |   4 +-
 biojava-sequencing/pom.xml                         |   4 +-
 biojava-structure-gui/pom.xml                      |   6 +-
 biojava-structure/pom.xml                          |   6 +-
 .../main/java/org/biojava/nbio/structure/Calc.java |   7 +-
 .../org/biojava/nbio/structure/NucleotideImpl.java |  33 +-
 .../biojava/nbio/structure/asa/AsaCalculator.java  |  12 +-
 .../structure/symmetry/core/HelixAxisAligner.java  |  11 +-
 .../structure/symmetry/core/PermutationGroup.java  |   8 +-
 .../symmetry/core/RotationAxisAligner.java         |  13 +-
 .../structure/symmetry/core/RotationGroup.java     |   8 +-
 .../structure/symmetry/core/RotationSolver.java    | 189 +++++++---
 .../nbio/structure/symmetry/core/Subunits.java     |   6 +
 .../symmetry/geometry/IcosahedralSampler.java      |  17 +-
 .../structure/symmetry/geometry/SphereSampler.java |  48 ++-
 biojava-survival/pom.xml                           |   2 +-
 biojava-ws/pom.xml                                 |   4 +-
 .../org/biojava/nbio/ws/hmmer/RemoteHmmerScan.java |  41 +--
 pom.xml                                            |   4 +-
 30 files changed, 742 insertions(+), 533 deletions(-)

diff --git a/biojava-aa-prop/pom.xml b/biojava-aa-prop/pom.xml
index 5c35a61..1a449c7 100644
--- a/biojava-aa-prop/pom.xml
+++ b/biojava-aa-prop/pom.xml
@@ -2,7 +2,7 @@
 	<parent>
 		<artifactId>biojava</artifactId>
 		<groupId>org.biojava</groupId>
-		<version>4.2.5</version>
+		<version>4.2.7</version>
 	</parent>
 	<modelVersion>4.0.0</modelVersion>
 	<artifactId>biojava-aa-prop</artifactId>
@@ -70,12 +70,12 @@
 		<dependency>
 			<groupId>org.biojava</groupId>
 			<artifactId>biojava-core</artifactId>
-			<version>4.2.5</version>
+			<version>4.2.7</version>
 		</dependency>
 		<dependency>
 			<groupId>org.biojava</groupId>
 			<artifactId>biojava-structure</artifactId>
-			<version>4.2.5</version>
+			<version>4.2.7</version>
 		</dependency>
 
 		<!-- logging dependencies (managed by parent pom, don't set versions or scopes here) -->
diff --git a/biojava-alignment/pom.xml b/biojava-alignment/pom.xml
index d42f48f..7dfe24c 100644
--- a/biojava-alignment/pom.xml
+++ b/biojava-alignment/pom.xml
@@ -4,7 +4,7 @@
 	<parent>
 		<artifactId>biojava</artifactId>
 		<groupId>org.biojava</groupId>
-		<version>4.2.5</version>
+		<version>4.2.7</version>
 	</parent>
 	<artifactId>biojava-alignment</artifactId>
 	<name>biojava-alignment</name>
@@ -46,7 +46,7 @@
 		<dependency>
 			<groupId>org.biojava</groupId>
 			<artifactId>biojava-core</artifactId>
-			<version>4.2.5</version>
+			<version>4.2.7</version>
 			<scope>compile</scope>
 		</dependency>
 		<dependency>
@@ -74,7 +74,7 @@
   		<dependency>
   			<groupId>org.biojava</groupId>
   			<artifactId>biojava-phylo</artifactId>
-  			<version>4.2.5</version>
+  			<version>4.2.7</version>
   		</dependency>
 	</dependencies>
 </project>
diff --git a/biojava-core/pom.xml b/biojava-core/pom.xml
index 80cab2a..90e465d 100644
--- a/biojava-core/pom.xml
+++ b/biojava-core/pom.xml
@@ -3,7 +3,7 @@
 	<parent>
 		<artifactId>biojava</artifactId>
 		<groupId>org.biojava</groupId>
-		<version>4.2.5</version>
+		<version>4.2.7</version>
 	</parent>
 	<modelVersion>4.0.0</modelVersion>
 	<artifactId>biojava-core</artifactId>
diff --git a/biojava-genome/pom.xml b/biojava-genome/pom.xml
index 93ca9d0..c82fade 100644
--- a/biojava-genome/pom.xml
+++ b/biojava-genome/pom.xml
@@ -3,7 +3,7 @@
 	<parent>
 		<artifactId>biojava</artifactId>
 		<groupId>org.biojava</groupId>
-		<version>4.2.5</version>
+		<version>4.2.7</version>
 	</parent>
 	<modelVersion>4.0.0</modelVersion>
 	<artifactId>biojava-genome</artifactId>
@@ -85,13 +85,13 @@
 		<dependency>
 			<groupId>org.biojava</groupId>
 			<artifactId>biojava-core</artifactId>
-			<version>4.2.5</version>
+			<version>4.2.7</version>
 			<scope>compile</scope>
 		</dependency>
 		<dependency>
 			<groupId>org.biojava</groupId>
 			<artifactId>biojava-alignment</artifactId>
-			<version>4.2.5</version>
+			<version>4.2.7</version>
 			<scope>compile</scope>
 		</dependency>
 		<dependency>
diff --git a/biojava-genome/src/main/java/org/biojava/nbio/genome/util/ChromosomeMappingTools.java b/biojava-genome/src/main/java/org/biojava/nbio/genome/util/ChromosomeMappingTools.java
index 07ace52..ea9538f 100644
--- a/biojava-genome/src/main/java/org/biojava/nbio/genome/util/ChromosomeMappingTools.java
+++ b/biojava-genome/src/main/java/org/biojava/nbio/genome/util/ChromosomeMappingTools.java
@@ -13,8 +13,11 @@ import java.util.ArrayList;
 import java.util.List;
 
 /**
- *  A class that take care of the painful mapping
+ *  A class that can map chromosomal positions to mRNA (coding sequence) positions.
+ *
+ *  @author Andreas Prlic
  */
+
 public class ChromosomeMappingTools {
 
     private static final Logger logger = LoggerFactory.getLogger(ChromosomeMappingTools.class);
@@ -915,7 +918,7 @@ public class ChromosomeMappingTools {
 //
 
     /**
-     * I have a genomic coordinate, where is it in the Gene?
+     * I have a genomic coordinate, where is it on the mRNA
      *
      * @param coordinate
      * @param chromosomePosition
@@ -924,7 +927,7 @@ public class ChromosomeMappingTools {
     public static int getCDSPosForChromosomeCoordinate(int coordinate, GeneChromosomePosition chromosomePosition) {
 
         if ( chromosomePosition.getOrientation() == '+')
-            return getCDSPosForward(coordinate,
+        	return getCDSPosForward(coordinate,
                     chromosomePosition.getExonStarts(),
                     chromosomePosition.getExonEnds(),
                     chromosomePosition.getCdsStart(),
@@ -935,301 +938,139 @@ public class ChromosomeMappingTools {
                 chromosomePosition.getExonEnds(),
                 chromosomePosition.getCdsStart(),
                 chromosomePosition.getCdsEnd());
-
     }
 
+	/** Converts the genetic coordinate to the position of the nucleotide on the mRNA sequence for a gene
+	 * living on the forward DNA strand.
+	 *
+	 * @param chromPos The genetic coordinate on a chromosome
+     * @param exonStarts The list holding the genetic coordinates pointing to the start positions of the exons (including UTR regions)
+     * @param exonEnds The list holding the genetic coordinates pointing to the end positions of the exons (including UTR regions)
+     * @param cdsStart The start position of a coding region
+     * @param cdsEnd The end position of a coding region
+     *
+     * @return the position of the nucleotide base on the mRNA sequence corresponding to the input genetic coordinate (base 1)
+	 *
+	 * @author Yana Valasatava
+	 */
+    public static int getCDSPosForward(int chromPos, List<Integer> exonStarts, List<Integer> exonEnds,
+            int cdsStart, int cdsEnd) {
 
-    public static int getCDSPosReverse(int chromPos, List<Integer> exonStarts, List<Integer> exonEnds,
-                                       int cdsStart, int cdsEnd) {
-        boolean inCoding = false;
-        int codingLength = 0;
-
-        if (cdsEnd < cdsStart) {
-            int tmp = cdsEnd;
-            cdsEnd = cdsStart;
-            cdsStart = tmp;
-        }
-
-        logger.debug("looking for CDS position for " +format(chromPos));
-
-
-        if ( chromPos <  cdsStart+1 ) {
-            // this is not in a coding region!
-
-            logger.debug(chromPos + " < " + cdsStart+1 );
+    	// the genetic coordinate is not in a coding region
+        if ( (chromPos < (cdsStart+1) ) || ( chromPos > (cdsEnd+1) ) ) {
+        	logger.debug("The "+format(chromPos)+" position is not in a coding region");
             return -1;
         }
 
-        if ( chromPos >  cdsEnd+1 ) {
-            // this is not in a coding region!
+        logger.debug("looking for CDS position for " +format(chromPos));
 
-            logger.debug(chromPos + " > " + cdsEnd+1 );
-            return -1;
+        // remove exons that are fully landed in UTRs
+        List<Integer> tmpS = new ArrayList<Integer>(exonStarts);
+        List<Integer> tmpE = new ArrayList<Integer>(exonEnds);
+
+        int j=0;
+        for (int i = 0; i < tmpS.size(); i++) {
+        	if ( ( tmpE.get(i) < cdsStart) || ( tmpS.get(i) > cdsEnd) ) {
+        		exonStarts.remove(j);
+        		exonEnds.remove(j);
+        	}
+        	else {
+        		j++;
+        	}
         }
 
-        int lengthExons = 0;
-
-        // map reverse
-        for (int i = exonStarts.size() - 1; i >= 0; i--) {
-
-            logger.debug("Reverse Exon #" + (i+1) + "/" + exonStarts.size());
-            int end = exonStarts.get(i);
-            int start = exonEnds.get(i);
-
-            if (end < start) {
-                int tmp = end;
-                end = start;
-                start = tmp;
-            }
-            lengthExons += end - start;
-
-
-            logger.debug("     is " + format(chromPos) + " part of Reverse exon? s:" + format(start+1) + " - e:" + format(end) + " | " + (end - start+1));
-            logger.debug("     CDS start: " + format(cdsStart+1) + "-" + format(cdsEnd) + " coding length counter:" + codingLength);
-
-
-
-            if (start+1 <= cdsEnd && end >= cdsEnd ) {
-
-                // first exon with UTR
-
-                inCoding = true;
-
-                int tmpstart = start;
-                if (start < cdsStart) {
-                    tmpstart = cdsStart;
-                }
-
-
-                logger.debug(" --- codingLength " + codingLength +
-                        " s:" +
-                        format(tmpstart+1) +
-                        " e:" +
-                        format(cdsEnd) +
-                        " p:" +
-                        format(chromPos) + " tmp: " + (chromPos - cdsStart));
-
-                logger.debug("check: " + (codingLength + cdsEnd - tmpstart+1) + " ==?? " + format(chromPos));
-
-                int tmp = cdsEnd - chromPos ;
-                // if (codingLength + cdsEnd - tmpstart >= chromPos) {
-                //if (end >= chromPos && start + (end-start) >= chromPos) {
-                // if (codingLength + cdsEnd - tmpstart >= chromPos) {
-                if ( chromPos >= start +1 && chromPos <= end){
-
-
-                    logger.debug(" -> found position in UTR exon:  P: " + format(chromPos) + " s:" + format(tmpstart+1) + " l:" + format(tmp) + " cdsS:" + format(cdsStart+1) + " cdsE:" + format(cdsEnd)  + " codingL:" + codingLength);
-                    return codingLength + tmp;
-                }
-
-
-                logger.debug("     codinglength " + codingLength + " + " + (cdsEnd - tmpstart ) );
-
-                // do not add 1 here
-                codingLength += (cdsEnd - tmpstart );
-
-                boolean debug = logger.isDebugEnabled();
-
-                if (debug) {
-                    StringBuffer b = new StringBuffer();
-                    b.append("     UTR         :" + format(cdsEnd + 1) + " - " + format(end) + newline);
-                    if (tmpstart == start)
-                        b.append(" ->  ");
-                    else
-                        b.append(" <-> ");
-                    b.append("Reverse Exon        :" + format(tmpstart+1) + " - " + (cdsEnd) + " | " + format(cdsEnd - tmpstart) + " - " + codingLength + " | " + (codingLength % 3) + newline);
-
-                    logger.debug(b.toString());
-
-                    // single exon with UTR on both ends
-                    if (tmpstart != start)
-                        logger.debug("     UTR         :" + format(cdsStart - 1) + " - " + format(start));
-                }
-            } else if (start <= cdsStart && end >= cdsStart) {
-
-                // terminal exon with UTR
-                inCoding = false;
-
-
-                logger.debug(format(start  + codingLength + end - cdsStart) + " ?? " + format(chromPos));
-                // (start  + codingLength + end - cdsStart >= chromPos &&
-                if (( start+1 <= chromPos) && ( end >= chromPos)) {
-
-                    //int tmp =  end - cdsStart ;
-//                    int tmp =  chromPos - cdsStart ;
-//                    int l = end - cdsStart;
-                    int tmp = end-chromPos ;
-                    if  ( tmp > end -cdsStart) {
-                        tmp = end-cdsStart ;
-
-                        logger.debug("Adjust tmp to " + tmp);
-                    }
-
-
-
-                    logger.debug(  codingLength + " | " + (end -chromPos) + " | " + (end - cdsStart) );
-                    logger.debug(" <-  Exon        : " + format(cdsStart) + " - " + format(end) + " | " + format(end - cdsStart +1) + " | ");
-                    logger.debug("     UTR         : " + format(start+1) + " - " + format(cdsStart ));
-                    logger.debug(" <- YYY found position noncoding exon:  #" + (i+1) + " "  + format(chromPos) + " s:" + format(start) + " tmp: " + format(tmp) + " cdsStart" + format(cdsStart) + " cdl:" + codingLength + " " + format(cdsEnd));
-
-                    return codingLength + tmp;
-                }
-
-
-                logger.debug("     codinglength " + codingLength + " + " + (end - cdsStart) );
-                codingLength += (end - cdsStart+1);
-
-                logger.debug(" <-  Exon        : " + format(cdsStart+1) + " - " + format(end) + " | " + format(end - cdsStart) + " | " + codingLength + " | " + (codingLength % 3));
-                logger.debug("     UTR         : " + format(start+1) + " - " + format(cdsStart ));
-
-            } else if (inCoding) {
-                // standard coding exon
-                // if (codingLength + end - start >= chromPos) {
-                if ( chromPos >= start+1 && chromPos <= end) {
-
-                    int tmp = end -chromPos  ;
-                    if ( tmp > (end-start+1)) {
-
-                        tmp = (end - start+1);
-
-                        logger.debug("Adjusting tmp to " + tmp);
-                    }
-
-
-                    logger.debug(" -> found position in reverse coding exon:  #" + (i+1) + " chromPos:"  + format(chromPos) + " start:" + format(start+1) + " end:" + format(end) + " tmp:" + tmp + " cdsStart:" + cdsStart + " codingLength:" + codingLength);
-
-                    return codingLength+tmp;
-                }
-
-                // full exon is coding
-
-                logger.debug("     codinglength " + codingLength + " + " + (end - start) );
-                // don't add 1
-                codingLength += (end - start);
-
-                logger.debug("     Exon        : " + format(start+1) + " - " + format(end) + " | " + format(end - start) + " | " + codingLength + " | " + (codingLength % 3));
-            } else {
-                // e.g. see UBQLN3
+        // remove untranslated regions from exons
+        int nExons = exonStarts.size();
+        exonStarts.remove(0);
+        exonStarts.add(0, cdsStart);
+        exonEnds.remove(nExons-1);
+        exonEnds.add(cdsEnd);
 
-                logger.debug(" no translation! cdl:" + codingLength);
-
-            }
+        int codingLength = 0;
+        int lengthExon = 0;
 
-            //if ( inCoding )
-            //	logger.debug("     exon phase at end:" + ((codingLength) % 3));
+        // map the genetic coordinate on a stretch of a reverse strand
+        for (int i = 0; i < nExons; i++) {
 
-            logger.debug("     coding length: " + codingLength + "(phase:" + (codingLength % 3) + ") CDS POS trying to map:" + chromPos);
+		    int start = exonStarts.get(i);
+		    int end = exonEnds.get(i);
 
+		    lengthExon = end - start;
 
+		    if (start+1 <= chromPos && end >= chromPos ) {
+		    	return codingLength + (chromPos-start);
+		    }
+	        else {
+	        	codingLength += lengthExon;
+	        }
         }
-
-        logger.debug("length exons: " + lengthExons);
-        // could not map, or map over the full length??
-
-
         return -1;
-
     }
 
-    /**
-     * Get the chromosome position mapped onto the mrna CDS transcript position (needs to be divided by 3 to get protein coordinate)
+	/** Converts the genetic coordinate to the position of the nucleotide on the mRNA sequence for a gene
+	 * living on the reverse DNA strand.
+	 *
+	 * @param chromPos The genetic coordinate on a chromosome
+     * @param exonStarts The list holding the genetic coordinates pointing to the start positions of the exons (including UTR regions)
+     * @param exonEnds The list holding the genetic coordinates pointing to the end positions of the exons (including UTR regions)
+     * @param cdsStart The start position of a coding region
+     * @param cdsEnd The end position of a coding region
      *
-     * @param exonStarts
-     * @param exonEnds
-     * @param cdsStart
-     * @param cdsEnd
-     * @return
-     */
-    public static int getCDSPosForward(int chromPos, List<Integer> exonStarts, List<Integer> exonEnds,
-                                       int cdsStart, int cdsEnd) {
-        boolean inCoding = false;
-        int codingLength = 0;
-
-
-        logger.debug("looking for CDS position for " +chromPos);
-
-        int lengthExons = 0;
-        // map forward
-        for (int i = 0; i < exonStarts.size(); i++) {
-
-
-            // start can include UTR
-            int start = exonStarts.get(i);
-            int end = exonEnds.get(i);
-
-            lengthExons += end - start;
-
-
-            logger.debug("forward exon: " + (start+1) + " - " + end + " | " + (end - start) + " ? overlaps with " + format(chromPos));
-
-            if (start +1 <= cdsStart +1 && end >= cdsStart+1) {
-
-                if (end >= chromPos) {
-                    // we are reaching our target position
-                    // -1 is important  here...
-                    int tmp = chromPos - cdsStart -1;
-
-
-                    logger.debug("cdl:" + codingLength + " | " + tmp);
-                    logger.debug(" -> found position in UTR exon:  " + chromPos + " " + format(start) + " " + format(tmp) + " " + cdsStart + " " + codingLength);
-
-                    return codingLength + tmp;
-                }
-                inCoding = true;
-                codingLength += (end - cdsStart);
-
-                logger.debug("     UTR         : " + format(start) + " - " + (cdsStart ));
-                logger.debug(" ->  Exon        : " + format(cdsStart+1) + " - " + format(end) + " | " + format(end - cdsStart) + " | " + codingLength + " | " + (codingLength % 3));
-
-            } else if (start <= cdsEnd && end >= cdsEnd) {
-                //logger.debug(" <-- CDS end at: " + cdsEnd );
-                inCoding = false;
-                if (cdsEnd >= chromPos && (start +1 <= chromPos)) {
-                    int tmp =  chromPos - start -1 ;
-
-
-                    logger.debug(" -> cdsForward found position in non coding exon#"+i+":  " + chromPos + " " + format(start+1) + " " + format(tmp) + " " + cdsStart   + " " + codingLength);
-                    return codingLength + tmp ;
-                }
-                codingLength += (cdsEnd - start);
-
-                logger.debug(" <-  Exon        : " + format(start+1) + " - " + format(cdsEnd) + " | " + format(cdsEnd - start+1) + " | " + codingLength + " | " + (codingLength % 3));
-                logger.debug("     UTR         : " + format(cdsEnd + 1) + " - " + format(end));
-
-
-            } else if (inCoding) {
-
-                if (end >= chromPos && (start +1 <=chromPos)) {
-
-                    int tmp = chromPos-start-1 ;
-
-
-                    logger.debug(codingLength + " | " + tmp);
-                    logger.debug(" -> found position in coding exon #" + (i + 1) + ":  " + format(chromPos) + " " + format(start + 1) + " " + format(tmp) + " " + cdsStart + " " + codingLength);
-
-
-                    return codingLength + tmp ;
-                }
-                // full exon is coding
-                codingLength += (end - start);
+     * @return the position of the nucleotide base on the mRNA sequence corresponding to the input genetic coordinate (base 1)
+	 *
+	 * @author Yana Valasatava
+	 */
+    public static int getCDSPosReverse(int chromPos, List<Integer> exonStarts, List<Integer> exonEnds,
+            int cdsStart, int cdsEnd) {
 
-                logger.debug("     Exon        :" + format(start) + " - " + format(end) + " | " + format(end - start) + " | " + codingLength + " | " + (codingLength % 3));
-            }
-            //			if ( inCoding )
-            //				logger.debug("exon phase at end:" + (codingLength % 3));
-            //
-            //			logger.debug("   coding length: " + codingLength);
+    	// the genetic coordinate is not in a coding region
+        if ( (chromPos < (cdsStart+1)) || ( chromPos > (cdsEnd+1) ) ) {
+        	logger.debug("The "+format(chromPos)+" position is not in a coding region");
+            return -1;
+        }
 
+        logger.debug("looking for CDS position for " +format(chromPos));
 
+        // remove exons that are fully landed in UTRs
+        List<Integer> tmpS = new ArrayList<Integer>(exonStarts);
+        List<Integer> tmpE = new ArrayList<Integer>(exonEnds);
+
+        int j=0;
+        for (int i = tmpS.size() - 1; i >= 0; i--) {
+        	if ( ( tmpE.get(i) < cdsStart) || ( tmpS.get(i) > cdsEnd) ) {
+        		exonStarts.remove(j);
+        		exonEnds.remove(j);
+        	}
+        	else {
+        		j++;
+        	}
         }
 
-        //logger.debug("length exons: " + lengthExons);
-        //return codingLength - 3;
-
-        // could not map!
+        // remove untranslated regions from exons
+        int nExons = exonStarts.size();
+        exonStarts.remove(0);
+        exonStarts.add(0, cdsStart);
+        exonEnds.remove(nExons-1);
+        exonEnds.add(cdsEnd);
 
+        int codingLength = 0;
+        int lengthExon = 0;
+        
+        // map the genetic coordinate on a stretch of a reverse strand
+        for (int i = nExons - 1; i >= 0; i--) {
+
+		    int start = exonStarts.get(i);
+		    int end = exonEnds.get(i);
+
+		    lengthExon = end - start;
+		    // +1 offset to be a base 1
+		    if (start+1 <= chromPos && end >= chromPos ) {
+		    	return codingLength + (end-chromPos+1);
+		    }
+	        else {
+	        	codingLength += lengthExon;
+	        }
+        }
         return -1;
     }
-
-
 }
diff --git a/biojava-genome/src/test/java/org/biojava/nbio/genome/TestGenomeMapping.java b/biojava-genome/src/test/java/org/biojava/nbio/genome/TestGenomeMapping.java
index b4615b9..7f9ded7 100644
--- a/biojava-genome/src/test/java/org/biojava/nbio/genome/TestGenomeMapping.java
+++ b/biojava-genome/src/test/java/org/biojava/nbio/genome/TestGenomeMapping.java
@@ -10,142 +10,306 @@ import org.junit.Test;
 
 import java.io.InputStream;
 import java.net.URL;
+import java.util.Arrays;
 import java.util.List;
 import java.util.zip.GZIPInputStream;
 
 /**
  * Created by andreas on 7/19/16.
  */
-public class TestGenomeMapping extends TestCase{
+public class TestGenomeMapping extends TestCase {
 
-    private static final String geneChromosomeFile = "http://cdn.rcsb.org/gene/hg38/geneChromosome38.tsf.gz";
+	private static final String geneChromosomeFile = "http://cdn.rcsb.org/gene/hg38/geneChromosome38.tsf.gz";
 
-    private List<GeneChromosomePosition> gcps = null;
+	private List<GeneChromosomePosition> gcps = null;
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        InputStream input = new GZIPInputStream(new URL(geneChromosomeFile).openStream());
-        gcps = GeneChromosomePositionParser.getChromosomeMappings(input);
+	@Override
+	protected void setUp() throws Exception {
+		super.setUp();
+		InputStream input = new GZIPInputStream(new URL(geneChromosomeFile).openStream());
+		gcps = GeneChromosomePositionParser.getChromosomeMappings(input);
+	}
 
+	@Test
+	public void testAK1() {
+		String geneName = "AK1";
 
-    }
+		assertNotNull(gcps);
+		assertTrue("Problems with downloading refFlat file from UCSC browser ", gcps.size() > 100);
 
+		int uniProtLength = 194;
 
-    @Test
-    public void testAK1() {
-        String geneName = "AK1";
+		try {
 
-        assertNotNull(gcps);
-        assertTrue("Problems with downloading refFlat file from UCSC browser ", gcps.size() > 100);
+			for (GeneChromosomePosition pos : gcps) {
 
-        int uniProtLength = 194;
+				//System.out.println(pos.getGeneName());
+				if (!pos.getGeneName().equals(geneName))
+					continue;
 
-        try {
+				/// there are three alternative transcripts for AK1.
+				// we are just testing one here:
 
-            for (GeneChromosomePosition pos : gcps) {
+				if ( ! pos.getGenebankId().equals("NM_000476"))
+					continue;
 
-                //System.out.println(pos.getGeneName());
-                if (!pos.getGeneName().equals(geneName))
-                    continue;
+				assertTrue(pos.getGeneName().equals(geneName));
+				assertTrue(pos.getOrientation().equals('-'));
+				assertTrue(pos.getChromosome().equals("chr9"));
 
-                /// there are three alternative transcripts for AK1.
-                // we are just testing one here:
+				List<Range<Integer>> cdsranges = ChromosomeMappingTools.getCDSExonRanges(pos);
 
-                if ( ! pos.getGenebankId().equals("NM_000476"))
-                    continue;
+				validateExon(0,0,7, cdsranges  );
+				validateExon(1,7,43, cdsranges  );
+				validateExon(2,43,207, cdsranges  );
+				validateExon(3,207,324, cdsranges  );
+				validateExon(4,324,516, cdsranges  );
+				validateExon(5,516,585, cdsranges  );
 
-                assertTrue(pos.getGeneName().equals(geneName));
-                assertTrue(pos.getOrientation().equals('-'));
-                assertTrue(pos.getChromosome().equals("chr9"));
 
-                List<Range<Integer>> cdsranges = ChromosomeMappingTools.getCDSExonRanges(pos);
+				int cdslength = ChromosomeMappingTools.getCDSLength(pos);
 
-                validateExon(0,0,7, cdsranges  );
-                validateExon(1,7,43, cdsranges  );
-                validateExon(2,43,207, cdsranges  );
-                validateExon(3,207,324, cdsranges  );
-                validateExon(4,324,516, cdsranges  );
-                validateExon(5,516,585, cdsranges  );
+				assertTrue("CDS length should be 582, but is " + cdslength, cdslength == (uniProtLength *3));
 
+				List<Range<Integer>> chromranges = ChromosomeMappingTools.getChromosomalRangesForCDS(pos);
 
-                int cdslength = ChromosomeMappingTools.getCDSLength(pos);
+				// we are reverse strand. reverse the order
+				chromranges = Lists.reverse(chromranges);
 
-                assertTrue("CDS length should be 582, but is " + cdslength, cdslength == (uniProtLength *3));
+				assertTrue(chromranges.size() == 6);
 
-                List<Range<Integer>> chromranges = ChromosomeMappingTools.getChromosomalRangesForCDS(pos);
+				// compare with https://www.ncbi.nlm.nih.gov/CCDS/CcdsBrowse.cgi?REQUEST=CCDS&DATA=CCDS6881
+				validateExon(0,127868008,127868076, chromranges  );
+				validateExon(1,127868320,127868512, chromranges  );
+				validateExon(2,127871822,127871939, chromranges  );
+				validateExon(3,127872689,127872853, chromranges  );
+				validateExon(4,127873025,127873061, chromranges  );
+				validateExon(5,127874610,127874617, chromranges  );
 
-                // we are reverse strand. reverse the order
-                chromranges = Lists.reverse(chromranges);
+			}
+		} catch (Exception e) {
+			fail(e.getMessage());
+		}
+	}
 
-                assertTrue(chromranges.size() == 6);
+	@Test
+	public void testHBA(){
 
-                // compare with https://www.ncbi.nlm.nih.gov/CCDS/CcdsBrowse.cgi?REQUEST=CCDS&DATA=CCDS6881
-                validateExon(0,127868008,127868076, chromranges  );
-                validateExon(1,127868320,127868512, chromranges  );
-                validateExon(2,127871822,127871939, chromranges  );
-                validateExon(3,127872689,127872853, chromranges  );
-                validateExon(4,127873025,127873061, chromranges  );
-                validateExon(5,127874610,127874617, chromranges  );
-
-            }
-        } catch (Exception e) {
-            fail(e.getMessage());
-        }
-    }
-
-    @Test
-    public void testHBA(){
-
-        String geneName = "HBA1";
-        assertNotNull(gcps);
-
-        assertTrue("Problems with downloading refFlat file from UCSC browser ", gcps.size() > 100);
-
-        try {
-
-            for ( GeneChromosomePosition pos : gcps){
-
-                //System.out.println(pos.getGeneName());
-                if ( ! pos.getGeneName().equals(geneName))
-                    continue;
-
-                assertTrue(pos.getGeneName().equals("HBA1"));
-                assertTrue(pos.getGenebankId().equals("NM_000558"));
-                assertTrue(pos.getChromosome().equals("chr16"));
-                assertTrue(pos.getTranscriptionStart().equals(176650));
-                assertTrue(pos.getTranscriptionEnd().equals(177522));
-                assertTrue(pos.getOrientation().equals('+'));
-
-                List<Range<Integer>> cdsranges = ChromosomeMappingTools.getCDSExonRanges(pos);
-
-                assertTrue(cdsranges.size() == 3);
-
-                validateExon(0,0,95,cdsranges);
-                validateExon(1,95,300,cdsranges);
-                validateExon(2,300,429,cdsranges);
-
-
-                List<Range<Integer>> chromranges = ChromosomeMappingTools.getChromosomalRangesForCDS(pos);
-
-                validateExon(0,176716,176811, chromranges  );
-                validateExon(1,176928,177133, chromranges  );
-                validateExon(2,177282,177411, chromranges  );
-
-
-            }
-        } catch (Exception e){
-            fail(e.getMessage());
-        }
-
-
-    }
-
-    private void validateExon(int exonNr, int start, int stop, List<Range<Integer>> cdsranges) {
-
-        Range exon = cdsranges.get(exonNr);
-        assertTrue("Exon " + exonNr + " boundary "+ exon.lowerEndpoint()  + " does not match " +start , exon.lowerEndpoint().equals(start));
-        assertTrue("Exon " + exonNr + " boundary " + exon.upperEndpoint() + " does not match " + stop, exon.upperEndpoint().equals(stop));
-
-    }
+		String geneName = "HBA1";
+		assertNotNull(gcps);
+
+		assertTrue("Problems with downloading refFlat file from UCSC browser ", gcps.size() > 100);
+
+		try {
+
+			for ( GeneChromosomePosition pos : gcps){
+
+				//System.out.println(pos.getGeneName());
+				if ( ! pos.getGeneName().equals(geneName))
+					continue;
+
+				assertTrue(pos.getGeneName().equals("HBA1"));
+				assertTrue(pos.getGenebankId().equals("NM_000558"));
+				assertTrue(pos.getChromosome().equals("chr16"));
+				assertTrue(pos.getTranscriptionStart().equals(176650));
+				assertTrue(pos.getTranscriptionEnd().equals(177522));
+				assertTrue(pos.getOrientation().equals('+'));
+
+				List<Range<Integer>> cdsranges = ChromosomeMappingTools.getCDSExonRanges(pos);
+
+				assertTrue(cdsranges.size() == 3);
+
+				validateExon(0,0,95,cdsranges);
+				validateExon(1,95,300,cdsranges);
+				validateExon(2,300,429,cdsranges);
+
+
+				List<Range<Integer>> chromranges = ChromosomeMappingTools.getChromosomalRangesForCDS(pos);
+
+				validateExon(0,176716,176811, chromranges  );
+				validateExon(1,176928,177133, chromranges  );
+				validateExon(2,177282,177411, chromranges  );
+
+
+			}
+		} catch (Exception e){
+			fail(e.getMessage());
+		}
+
+
+	}
+
+	private void validateExon(int exonNr, int start, int stop, List<Range<Integer>> cdsranges) {
+
+		Range<Integer> exon = cdsranges.get(exonNr);
+		assertTrue("Exon " + exonNr + " boundary "+ exon.lowerEndpoint()  + " does not match " +start , exon.lowerEndpoint().equals(start));
+		assertTrue("Exon " + exonNr + " boundary " + exon.upperEndpoint() + " does not match " + stop, exon.upperEndpoint().equals(stop));
+
+	}
+
+	/** Get the position of the nucleotide base corresponding to the position of that base on the mRNA sequence 
+	 * for a gene living on the reverse DNA strand. 
+	 * 
+	 * @author Yana Valasatava
+	 */
+	private int getPositionInmRNA(String geneName, String genebankId, int posChrom) {
+		for (GeneChromosomePosition gcp : gcps) {	
+			if ( gcp.getGeneName().equals(geneName) ) {
+				if ( gcp.getGenebankId().equals(genebankId) ) {
+					return ChromosomeMappingTools.getCDSPosForChromosomeCoordinate(posChrom, gcp);
+				}
+			}	
+		}
+		return -1;
+	}
+	
+	/** Make sure the mapping tool correctly retrieves the mRNA position for a gene 
+	 * living on the forward DNA strand for different chromosome positions. 
+	 * 
+	 * @author Yana Valasatava
+	 */
+	@Test
+	public void testForwardMappingPositions() {
+
+		String geneName = "HORMAD2"; // gene on the forward DNA strand 
+		String genebankId = "NM_152510"; // GeneBank ID for the transcript used for testing (ENST00000336726)
+		
+		List<String> scenarios = Arrays.asList("first1exon", "last1exon", "last3exon");
+		
+		int cds;
+		int posExonStart;
+		int posInmRNA;
+		for (String scenario : scenarios) {
+			
+			switch (scenario) {
+				
+				case "first1exon":
+			    	posExonStart = 30093953; // ending position of the last exon coding region (on forward strand)
+			    	posInmRNA = 1; // base 1 position in mRNA sequence
+					cds = getPositionInmRNA(geneName, genebankId, posExonStart);
+					assertEquals(cds, posInmRNA);
+					break;
+					
+				case "last1exon":
+			    	posExonStart = 30094003; // starting position of the last exon coding region (on forward strand)
+			    	posInmRNA = 51; // position in mRNA sequence equals to the length of the exon
+					cds = getPositionInmRNA(geneName, genebankId, posExonStart);
+					assertEquals(cds, posInmRNA);		
+					break;
+				
+				case "last3exon":
+					posExonStart = 30103500; // starting position of the first base in a coding region (3rd exon)
+					posInmRNA = 257; // position in mRNA sequence equals to the sum length of the 3 last exons 
+					cds = getPositionInmRNA(geneName, genebankId, posExonStart);
+					assertEquals(cds, posInmRNA);
+					break;
+			}
+		}
+	}
+		
+	/** Make sure the mapping tool correctly retrieves the mRNA position for a gene 
+	 * living on the reverse DNA strand for different chromosome positions. 
+	 * 
+	 * @author Yana Valasatava
+	 */
+	@Test
+	public void testReverseMappingPositions() {
+
+		String geneName = "BCL11B"; // gene on the reverse DNA strand 
+		String genebankId = "NM_138576"; // GeneBank ID for the transcript used for testing (ENST00000357195)
+		
+		List<String> scenarios = Arrays.asList("first1exon", "last1exon", "last3exon");
+		
+		int cds;
+		int posExonStart;
+		int posInmRNA;
+		for (String scenario : scenarios) {
+			
+			switch (scenario) {
+				
+				case "first1exon":
+			    	posExonStart = 99271218; // ending position of the last exon coding region (on forward strand)
+			    	posInmRNA = 1; // base 1 position in mRNA sequence
+					cds = getPositionInmRNA(geneName, genebankId, posExonStart);
+					assertEquals(cds, posInmRNA);
+					break;
+					
+				case "last1exon":
+			    	posExonStart = 99271161; // starting position of the last exon coding region (on forward strand)
+			    	posInmRNA = 58; // position in mRNA sequence equals to the length of the exon
+					cds = getPositionInmRNA(geneName, genebankId, posExonStart);
+					assertEquals(cds, posInmRNA);		
+					break;
+				
+				case "last3exon":
+					posExonStart = 99231345; // starting position of the first base in a coding region (3rd exon)
+					posInmRNA = 640; // position in mRNA sequence equals to the sum length of the 3 last exons 
+					cds = getPositionInmRNA(geneName, genebankId, posExonStart);
+					assertEquals(cds, posInmRNA);
+					break;
+			}
+		}
+	}
+
+	/** Test to make sure the mapping tool correctly identify that position falls outside the coding region 
+	 * for a gene living on the forward DNA strand.
+	 * 
+	 * @author Yana Valasatava
+	 */
+	@Test
+	public void testForwardMappingForExonBoundaries() {
+
+		String geneName = "HBA1"; // gene on the reverse DNA strand 
+		String genebankId = "NM_000558"; // GeneBank ID for the transcript used for testing (ENST00000320868)
+
+		int posExonStart = 176717; // starting position of the first base in a coding region (1st exon)
+		int posExonEnd = 176811; // ending position of the first base in a coding region (1st exon)
+
+		int cdsSE = getPositionInmRNA(geneName, genebankId, posExonStart-1);
+		assertEquals(cdsSE, -1);
+
+		int cdsEE = getPositionInmRNA(geneName, genebankId, posExonEnd+1);
+		assertEquals(cdsEE, -1);
+	}
+		
+	/** Test to make sure the mapping tool correctly identify that position falls outside the coding region 
+	 * for a gene living on the reverse DNA strand.
+	 * 
+	 * @author Yana Valasatava
+	 */
+	@Test
+	public void testReverseMappingForExonBoundaries() {
+
+		String geneName = "BCL11B"; // gene on the reverse DNA strand 
+		String genebankId = "NM_138576"; // GeneBank ID for the transcript used for testing (ENST00000357195)
+
+		int posExonStart = 99174151; // starting position of the first base in a coding region (1st exon)
+		int posExonEnd = 99176195; // ending position of the first base in a coding region (1st exon)
+		
+		int cdsSE = getPositionInmRNA(geneName, genebankId, posExonStart-1);
+		assertEquals(cdsSE, -1);
+		
+		int cdsEE = getPositionInmRNA(geneName, genebankId, posExonEnd+1);
+		assertEquals(cdsEE, -1);	
+	}
+	
+	/** Test to make sure the mapping tool correctly converts the genetic position to a position on mRNA 
+	 * when multiple UTR regions are consecutive.
+	 * 
+	 * @author Yana Valasatava
+	 */
+	@Test
+	public void testMappingCromosomePosTomRNAMultiUTRs() {
+
+		String geneName = "ILK"; // gene on the reverse DNA strand 
+		String genebankId = "NM_001278442"; // GeneBank ID for the transcript used for testing (ENST00000532063)
+
+		int chromPos = 6608760;
+		int mRNAPos = 16;
+				
+		int cds = getPositionInmRNA(geneName, genebankId, chromPos);
+		assertEquals(cds, mRNAPos);
+		
+	}
 }
+
diff --git a/biojava-integrationtest/pom.xml b/biojava-integrationtest/pom.xml
index 82703c0..c52783d 100644
--- a/biojava-integrationtest/pom.xml
+++ b/biojava-integrationtest/pom.xml
@@ -4,7 +4,7 @@
   <parent>
     <artifactId>biojava</artifactId>
     <groupId>org.biojava</groupId>
-    <version>4.2.5</version>
+    <version>4.2.7</version>
   </parent>
   <artifactId>biojava-integrationtest</artifactId>
   <packaging>jar</packaging>
@@ -32,7 +32,7 @@
     <dependency>
     	<groupId>org.biojava</groupId>
     	<artifactId>biojava-structure</artifactId>
-    	<version>4.2.5</version>
+    	<version>4.2.7</version>
     </dependency>
 		<!-- logging dependencies (managed by parent pom, don't set versions or scopes here) -->
 		<dependency>
diff --git a/biojava-integrationtest/src/test/java/org/biojava/nbio/structure/test/StructureToolsTest.java b/biojava-integrationtest/src/test/java/org/biojava/nbio/structure/test/StructureToolsTest.java
index de234a7..7faf37e 100644
--- a/biojava-integrationtest/src/test/java/org/biojava/nbio/structure/test/StructureToolsTest.java
+++ b/biojava-integrationtest/src/test/java/org/biojava/nbio/structure/test/StructureToolsTest.java
@@ -22,8 +22,6 @@
  */
 package org.biojava.nbio.structure.test;
 
-import junit.framework.TestCase;
-
 import org.biojava.nbio.structure.*;
 import org.biojava.nbio.structure.align.util.AtomCache;
 import org.biojava.nbio.structure.io.FileParsingParameters;
@@ -31,18 +29,21 @@ import org.biojava.nbio.structure.io.PDBFileParser;
 import org.biojava.nbio.structure.io.mmcif.ChemCompGroupFactory;
 import org.biojava.nbio.structure.io.mmcif.ChemCompProvider;
 import org.biojava.nbio.structure.io.mmcif.DownloadChemCompProvider;
+import org.junit.Before;
+import org.junit.Test;
 
 import java.io.IOException;
 import java.io.InputStream;
 
 import static org.junit.Assume.assumeNoException;
+import static org.junit.Assert.*;
 
-public class StructureToolsTest extends TestCase {
+public class StructureToolsTest {
 
 	Structure structure, structure2, structure3, structure4;
 
-	@Override
-	protected void setUp() throws IOException
+	@Before
+	public void setUp() throws IOException
 	{
 		InputStream inStream = this.getClass().getResourceAsStream("/5pti.pdb");
 		assertNotNull(inStream);
@@ -85,12 +86,13 @@ public class StructureToolsTest extends TestCase {
 		inStream.close();
 	}
 
-
+	@Test
 	public void testGetCAAtoms(){
 		Atom[] cas = StructureTools.getRepresentativeAtomArray(structure);
 		assertEquals("did not find the expected number of Atoms (58), but got " + cas.length,58,cas.length);
 	}
 
+	@Test
 	public void testGetAtomsConsistency() throws IOException, StructureException{
 
 		//Save the existing ChemCompProvider
@@ -120,11 +122,13 @@ public class StructureToolsTest extends TestCase {
 		ChemCompGroupFactory.setChemCompProvider(provider);
 	}
 
+	@Test
 	public void testGetNrAtoms(){
 		int length = StructureTools.getNrAtoms(structure);
 		assertEquals("did not find the expected number of Atoms (1087), but got " + length,1087,length);
 	}
 
+	@Test
 	@SuppressWarnings("deprecation")
 	public void testGetSubRanges() throws StructureException {
 		String range;
@@ -239,6 +243,7 @@ public class StructureToolsTest extends TestCase {
 		} catch(IllegalArgumentException ex) {} //expected
 	}
 
+	@Test
 	public void testRevisedConvention() throws IOException, StructureException{
 
 		AtomCache cache = new AtomCache();
@@ -319,6 +324,7 @@ public class StructureToolsTest extends TestCase {
 	 * Test some subranges that we used to have problems with
 	 * @throws StructureException
 	 */
+	@Test
 	@SuppressWarnings("deprecation")
 	public void testGetSubRangesExtended() throws StructureException {
 		String range;
@@ -380,6 +386,7 @@ public class StructureToolsTest extends TestCase {
 	 * Test insertion codes
 	 * @throws StructureException
 	 */
+	@Test
 	@SuppressWarnings("deprecation")
 	public void testGetSubRangesInsertionCodes() throws StructureException {
 		String range;
@@ -437,6 +444,7 @@ public class StructureToolsTest extends TestCase {
 		//TODO
 	}
 
+	@Test
 	public void testCAmmCIF() throws StructureException {
 
 		//Save the existing ChemCompProvider
@@ -473,5 +481,36 @@ public class StructureToolsTest extends TestCase {
 
 		ChemCompGroupFactory.setChemCompProvider(provider);
 	}
+	
+	@Test
+	public void testGetRepresentativeAtomsProtein() throws StructureException, IOException {
+		Structure s = StructureIO.getStructure("1smt");
+		Chain c = s.getChain(0);
+		Atom[] atoms = StructureTools.getRepresentativeAtomArray(c);
+		assertEquals(98,atoms.length);
+		
+		Chain clonedChain = (Chain)c.clone();
+		atoms = StructureTools.getRepresentativeAtomArray(clonedChain); 
+		assertEquals(98,atoms.length);
+	}
 
+	/**
+	 * See https://github.com/biojava/biojava/issues/631
+	 * @throws StructureException
+	 * @throws IOException
+	 */
+	@Test
+	public void testGetRepresentativeAtomsDna() throws StructureException, IOException {
+	
+		Structure s = StructureIO.getStructure("2pvi");
+		Chain c = s.getChainByPDB("C");
+		Atom[] atoms = StructureTools.getRepresentativeAtomArray(c); // chain C (1st nucleotide chain)
+		// actually it should be 13, but at the moment one of the nucleotides is not caught correctly because it's non-standard
+		assertEquals(12,atoms.length);
+		
+		Chain clonedChain = (Chain)c.clone();
+		atoms = StructureTools.getRepresentativeAtomArray(clonedChain); // chain C (1st nucleotide chain)
+		assertEquals(12,atoms.length);
+		
+	}
 }
diff --git a/biojava-modfinder/pom.xml b/biojava-modfinder/pom.xml
index a1f7b57..0ca1247 100644
--- a/biojava-modfinder/pom.xml
+++ b/biojava-modfinder/pom.xml
@@ -4,7 +4,7 @@
   <parent>
     <artifactId>biojava</artifactId>
     <groupId>org.biojava</groupId>
-    <version>4.2.5</version>
+    <version>4.2.7</version>
   </parent>
   <artifactId>biojava-modfinder</artifactId>
   <name>biojava-modfinder</name>
@@ -31,7 +31,7 @@
     <dependency>
     	<groupId>org.biojava</groupId>
     	<artifactId>biojava-structure</artifactId>
-    	<version>4.2.5</version>
+    	<version>4.2.7</version>
     	<type>jar</type>
     	<scope>compile</scope>
     </dependency>
diff --git a/biojava-ontology/pom.xml b/biojava-ontology/pom.xml
index 441b3bd..03b1c00 100644
--- a/biojava-ontology/pom.xml
+++ b/biojava-ontology/pom.xml
@@ -4,7 +4,7 @@
   <parent>
     <groupId>org.biojava</groupId>
     <artifactId>biojava</artifactId>
-    <version>4.2.5</version>
+    <version>4.2.7</version>
   </parent>
 
   <artifactId>biojava-ontology</artifactId>
diff --git a/biojava-phylo/pom.xml b/biojava-phylo/pom.xml
index affe865..d374a99 100644
--- a/biojava-phylo/pom.xml
+++ b/biojava-phylo/pom.xml
@@ -3,7 +3,7 @@
   <parent>
     <artifactId>biojava</artifactId>
     <groupId>org.biojava</groupId>
-    <version>4.2.5</version>
+    <version>4.2.7</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>biojava-phylo</artifactId>
@@ -44,7 +44,7 @@
     <dependency>
       <groupId>org.biojava</groupId>
       <artifactId>biojava-core</artifactId>
-      <version>4.2.5</version>
+      <version>4.2.7</version>
       <scope>compile</scope>
     </dependency>
     <dependency>
diff --git a/biojava-protein-disorder/pom.xml b/biojava-protein-disorder/pom.xml
index 9e12925..73c642d 100644
--- a/biojava-protein-disorder/pom.xml
+++ b/biojava-protein-disorder/pom.xml
@@ -3,7 +3,7 @@
 	<parent>
 		<artifactId>biojava</artifactId>
 		<groupId>org.biojava</groupId>
-		<version>4.2.5</version>
+		<version>4.2.7</version>
 	</parent>
 	<artifactId>biojava-protein-disorder</artifactId>
 	<packaging>jar</packaging>
@@ -63,7 +63,7 @@
 		<dependency>
 			<groupId>org.biojava</groupId>
 			<artifactId>biojava-core</artifactId>
-			<version>4.2.5</version>
+			<version>4.2.7</version>
 		</dependency>
 		<!-- logging dependencies (managed by parent pom, don't set versions or 
 			scopes here) -->
diff --git a/biojava-sequencing/pom.xml b/biojava-sequencing/pom.xml
index eb65c46..0b4d66e 100644
--- a/biojava-sequencing/pom.xml
+++ b/biojava-sequencing/pom.xml
@@ -3,7 +3,7 @@
 	<parent>
 		<artifactId>biojava</artifactId>
 		<groupId>org.biojava</groupId>
-		<version>4.2.5</version>
+		<version>4.2.7</version>
 	</parent>
 	<modelVersion>4.0.0</modelVersion>
 	<artifactId>biojava-sequencing</artifactId>
@@ -47,7 +47,7 @@
 		<dependency>
 			<groupId>org.biojava</groupId>
 			<artifactId>biojava-core</artifactId>
-			<version>4.2.5</version>
+			<version>4.2.7</version>
 			<scope>compile</scope>
 		</dependency>
 		<!-- logging dependencies (managed by parent pom, don't set versions or scopes here) -->
diff --git a/biojava-structure-gui/pom.xml b/biojava-structure-gui/pom.xml
index d5b18c9..cfd684d 100644
--- a/biojava-structure-gui/pom.xml
+++ b/biojava-structure-gui/pom.xml
@@ -3,7 +3,7 @@
     <parent>
         <artifactId>biojava</artifactId>
         <groupId>org.biojava</groupId>
-        <version>4.2.5</version>
+        <version>4.2.7</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <artifactId>biojava-structure-gui</artifactId>
@@ -25,13 +25,13 @@
         <dependency>
             <groupId>org.biojava</groupId>
             <artifactId>biojava-structure</artifactId>
-            <version>4.2.5</version>
+            <version>4.2.7</version>
             <scope>compile</scope>
         </dependency>
         <dependency>
             <groupId>org.biojava</groupId>
             <artifactId>biojava-core</artifactId>
-            <version>4.2.5</version>
+            <version>4.2.7</version>
             <scope>compile</scope>
         </dependency>
 
diff --git a/biojava-structure/pom.xml b/biojava-structure/pom.xml
index 0be0265..db1c41a 100644
--- a/biojava-structure/pom.xml
+++ b/biojava-structure/pom.xml
@@ -4,7 +4,7 @@
 	<parent>
 		<artifactId>biojava</artifactId>
 		<groupId>org.biojava</groupId>
-		<version>4.2.5</version>
+		<version>4.2.7</version>
 	</parent>
 	<artifactId>biojava-structure</artifactId>
 	<name>biojava-structure</name>
@@ -22,13 +22,13 @@
 		<dependency>
 			<groupId>org.biojava</groupId>
 			<artifactId>biojava-alignment</artifactId>
-			<version>4.2.5</version>
+			<version>4.2.7</version>
 			<scope>compile</scope>
 		</dependency>
 		<dependency>
 			<groupId>org.biojava</groupId>
 			<artifactId>biojava-core</artifactId>
-			<version>4.2.5</version>
+			<version>4.2.7</version>
 			<scope>compile</scope>
 		</dependency>
 
diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/Calc.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/Calc.java
index dee571a..d09252f 100644
--- a/biojava-structure/src/main/java/org/biojava/nbio/structure/Calc.java
+++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/Calc.java
@@ -699,12 +699,17 @@ public class Calc {
 
 
 
-	/** Returns the center  of mass of the set of atoms.
+	/** 
+	 * Returns the center  of mass of the set of atoms.
 	 * @param atomSet a set of Atoms
 	 * @return an Atom representing the Centroid of the set of atoms
 	 */
 	public static final Atom getCentroid(Atom[] atomSet){
 
+		// if we don't catch this case, the centroid returned is (NaN,NaN,NaN), which can cause lots of problems down the line
+		if (atomSet.length==0) 
+			throw new IllegalArgumentException("Atom array has length 0, can't calculate centroid!");
+		
 		double[] coords = new double[3];
 
 		coords[0] = 0;
diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/NucleotideImpl.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/NucleotideImpl.java
index 7cc5a94..75fa3b8 100644
--- a/biojava-structure/src/main/java/org/biojava/nbio/structure/NucleotideImpl.java
+++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/NucleotideImpl.java
@@ -93,5 +93,36 @@ public class NucleotideImpl extends HetatomImpl implements Group, Serializable,
 
 	}
 
-	// no need to implement clone here, it's already in super class
+	// note we need to implement a clone here, despite there's one in super class already,
+	// that's due to issue https://github.com/biojava/biojava/issues/631 - JD 2017-01-21
+	@Override
+	public Object clone() {
+
+		NucleotideImpl n = new NucleotideImpl();
+		n.setPDBFlag(has3D());
+		n.setResidueNumber(getResidueNumber());
+
+		n.setPDBName(getPDBName());
+
+		// copy the atoms
+		for (Atom atom1 : atoms) {
+			Atom atom = (Atom) atom1.clone();
+			n.addAtom(atom);
+			atom.setGroup(n);
+		}
+
+		// copying the alt loc groups if present, otherwise they stay null
+		if (getAltLocs()!=null && !getAltLocs().isEmpty()) {
+			for (Group altLocGroup:this.getAltLocs()) {
+				Group nAltLocGroup = (Group)altLocGroup.clone();
+				n.addAltLoc(nAltLocGroup);
+			}
+		}
+		
+		if (chemComp!=null)
+			n.setChemComp(chemComp);
+
+
+		return n;
+	}
 }
diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/asa/AsaCalculator.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/asa/AsaCalculator.java
index c5d3966..b5c66e6 100644
--- a/biojava-structure/src/main/java/org/biojava/nbio/structure/asa/AsaCalculator.java
+++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/asa/AsaCalculator.java
@@ -411,7 +411,7 @@ public class AsaCalculator {
 					else if (atomCode.equals("CG")) return TETRAHEDRAL_CARBON_VDW;
 
 				default:
-					logger.warn("Unexpected carbon atom "+atomCode+" for aminoacid "+aa+", assigning its standard vdw radius");
+					logger.info("Unexpected carbon atom "+atomCode+" for aminoacid "+aa+", assigning its standard vdw radius");
 					return Element.C.getVDWRadius();
 				}
 			}
@@ -419,12 +419,8 @@ public class AsaCalculator {
 			// not any of the expected atoms
 		} else {
 			// non standard aas, (e.g. MSE, LLP) will always have this problem,
-			// thus we log at info level for them, at warn for others
-			if (amino.getChemComp()!=null && amino.getChemComp().isStandard()) {
-				logger.warn("Unexpected atom "+atomCode+" for aminoacid "+aa+ " ("+amino.getPDBName()+"), assigning its standard vdw radius");
-			} else {
-				logger.info("Unexpected atom "+atomCode+" for aminoacid "+aa+ " ("+amino.getPDBName()+"), assigning its standard vdw radius");
-			}
+			logger.info("Unexpected atom "+atomCode+" for aminoacid "+aa+ " ("+amino.getPDBName()+"), assigning its standard vdw radius");
+			
 
 			return atom.getElement().getVDWRadius();
 		}
@@ -449,7 +445,7 @@ public class AsaCalculator {
 
 		if (atom.getElement()==Element.O) return OXIGEN_VDW;
 
-		logger.warn("Unexpected atom "+atom.getName()+" for nucleotide "+nuc.getPDBName()+", assigning its standard vdw radius");
+		logger.info("Unexpected atom "+atom.getName()+" for nucleotide "+nuc.getPDBName()+", assigning its standard vdw radius");
 		return atom.getElement().getVDWRadius();
 	}
 
diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/HelixAxisAligner.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/HelixAxisAligner.java
index 6685e6a..8e69ea8 100644
--- a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/HelixAxisAligner.java
+++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/HelixAxisAligner.java
@@ -22,11 +22,16 @@ package org.biojava.nbio.structure.symmetry.core;
 
 import org.biojava.nbio.structure.symmetry.geometry.MomentsOfInertia;
 import org.biojava.nbio.structure.symmetry.geometry.SuperPosition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import javax.vecmath.*;
 import java.util.*;
 
 public class HelixAxisAligner extends AxisAligner {
+	
+	private static final Logger LOGGER = LoggerFactory.getLogger(HelixAxisAligner.class);
+	
 	private static final Vector3d Y_AXIS = new Vector3d(0,1,0);
 	private static final Vector3d Z_AXIS = new Vector3d(0,0,1);
 
@@ -480,7 +485,7 @@ public class HelixAxisAligner extends AxisAligner {
 		ref[0] = new Point3d(referenceVectors[0]);
 		ref[1] = new Point3d(referenceVectors[1]);
 		if (SuperPosition.rmsd(axes, ref) > 0.1) {
-			System.out.println("Warning: AxisTransformation: axes alignment is off. RMSD: " + SuperPosition.rmsd(axes, ref));
+			LOGGER.info("AxisTransformation: axes alignment is off. RMSD: " + SuperPosition.rmsd(axes, ref));
 		}
 
 		return m2;
@@ -600,7 +605,7 @@ public class HelixAxisAligner extends AxisAligner {
 		referenceVector = getReferenceAxisCylic();
 
 		if (referenceVector == null) {
-			System.err.println("Warning: no reference vector found. Using y-axis.");
+			LOGGER.warn("no reference vector found. Using y-axis.");
 			referenceVector = new Vector3d(Y_AXIS);
 		}
 		// make sure reference vector is perpendicular principal roation vector
@@ -616,7 +621,7 @@ public class HelixAxisAligner extends AxisAligner {
 			vector2.negate();
 		}
 		if (Math.abs(dot) < 0.00001) {
-			System.out.println("HelixAxisAligner: Warning: reference axis parallel");
+			LOGGER.info("HelixAxisAligner: reference axis parallel");
 		}
 		vector2.cross(vector1, vector2);
 //		System.out.println("Intermed. refVector: " + vector2);
diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/PermutationGroup.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/PermutationGroup.java
index 675e4fa..19f10ba 100644
--- a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/PermutationGroup.java
+++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/PermutationGroup.java
@@ -23,6 +23,7 @@ package org.biojava.nbio.structure.symmetry.core;
 
 import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
 
@@ -30,7 +31,7 @@ import java.util.Set;
  *
  * @author Peter
  */
-public class PermutationGroup {
+public class PermutationGroup implements Iterable<List<Integer>> {
 	List<List<Integer>> permutations = new ArrayList<List<Integer>>();
 
 	public void addPermutation(List<Integer> permutation) {
@@ -139,4 +140,9 @@ public class PermutationGroup {
 	public int hashCode() {
 		return getGroupTable().hashCode();
 	}
+
+	@Override
+	public Iterator<List<Integer>> iterator() {
+		return permutations.iterator();
+	}
 }
diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/RotationAxisAligner.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/RotationAxisAligner.java
index 12d5db9..1a5bf33 100644
--- a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/RotationAxisAligner.java
+++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/RotationAxisAligner.java
@@ -22,11 +22,16 @@ package org.biojava.nbio.structure.symmetry.core;
 
 import org.biojava.nbio.structure.symmetry.geometry.MomentsOfInertia;
 import org.biojava.nbio.structure.symmetry.geometry.SuperPosition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import javax.vecmath.*;
 import java.util.*;
 
 public class RotationAxisAligner extends AxisAligner{
+	
+	private static final Logger LOGGER = LoggerFactory.getLogger(RotationAxisAligner.class);
+	
 	private static final Vector3d X_AXIS = new Vector3d(1,0,0);
 	private static final Vector3d Y_AXIS = new Vector3d(0,1,0);
 	private static final Vector3d Z_AXIS = new Vector3d(0,0,1);
@@ -320,7 +325,7 @@ public class RotationAxisAligner extends AxisAligner{
 		}
 		Collections.reverse(orbit.subList(1,  orbit.size()));
 		if (orbit.get(1) != index2) {
-			System.err.println("Warning: alignWithReferenceAxis failed");
+			LOGGER.warn("alignWithReferenceAxis failed");
 		}
 //		System.out.println("Orbit2: " + orbit);
 		return orbit;
@@ -459,7 +464,7 @@ public class RotationAxisAligner extends AxisAligner{
 		ref[0] = new Point3d(referenceVectors[0]);
 		ref[1] = new Point3d(referenceVectors[1]);
 		if (SuperPosition.rmsd(axes, ref) > 0.1) {
-			System.out.println("Warning: AxisTransformation: axes alignment is off. RMSD: " + SuperPosition.rmsd(axes, ref));
+			LOGGER.info("AxisTransformation: axes alignment is off. RMSD: " + SuperPosition.rmsd(axes, ref));
 		}
 
 		return m2;
@@ -632,7 +637,7 @@ public class RotationAxisAligner extends AxisAligner{
 			int index = p0.indexOf(current);
 			int next = p1.get(index);
 			if (!orbit.contains(next)) {
-				System.err.println("deconvolute: inconsistency in permuation. Returning original order");
+				LOGGER.warn("deconvolute: inconsistency in permuation. Returning original order");
 				return orbit;
 			}
 			inRotationOrder.add(next);
@@ -675,7 +680,7 @@ public class RotationAxisAligner extends AxisAligner{
 		}
 
 		if (referenceVector == null) {
-			System.err.println("Warning: no reference vector found. Using y-axis.");
+			LOGGER.warn("no reference vector found. Using y-axis.");
 			referenceVector = new Vector3d(Y_AXIS);
 		}
 		// make sure reference vector is perpendicular principal roation vector
diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/RotationGroup.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/RotationGroup.java
index ead65b5..c605aa2 100644
--- a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/RotationGroup.java
+++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/RotationGroup.java
@@ -27,13 +27,14 @@ import javax.vecmath.Vector3d;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.Iterator;
 import java.util.List;
 
 /**
  *
  * @author Peter
  */
-public class RotationGroup {
+public class RotationGroup implements Iterable<Rotation> {
 	private List<Rotation> rotations = new ArrayList<Rotation>();
 	private int principalAxisIndex = 0;
 	private int higherOrderRotationAxis = 0;
@@ -369,4 +370,9 @@ public class RotationGroup {
 			}
 		});
 	}
+
+	@Override
+	public Iterator<Rotation> iterator() {
+		return rotations.iterator();
+	}
 }
diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/RotationSolver.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/RotationSolver.java
index 8802ef3..bbc0d4c 100644
--- a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/RotationSolver.java
+++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/RotationSolver.java
@@ -21,19 +21,22 @@
 
 package org.biojava.nbio.structure.symmetry.core;
 
-import org.biojava.nbio.structure.symmetry.geometry.DistanceBox;
-import org.biojava.nbio.structure.symmetry.geometry.MomentsOfInertia;
-import org.biojava.nbio.structure.symmetry.geometry.SphereSampler;
-import org.biojava.nbio.structure.symmetry.geometry.SuperPosition;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 import javax.vecmath.AxisAngle4d;
 import javax.vecmath.Matrix4d;
 import javax.vecmath.Point3d;
 import javax.vecmath.Vector3d;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
+
+import org.biojava.nbio.structure.symmetry.geometry.DistanceBox;
+import org.biojava.nbio.structure.symmetry.geometry.MomentsOfInertia;
+import org.biojava.nbio.structure.symmetry.geometry.SphereSampler;
+import org.biojava.nbio.structure.symmetry.geometry.SuperPosition;
 
 
 /**
@@ -50,7 +53,8 @@ public class RotationSolver implements QuatSymmetrySolver {
 	private Matrix4d centroidInverse = new Matrix4d();
 	private Point3d[] originalCoords = null;
 	private Point3d[] transformedCoords = null;
-	private Set<List<Integer>> hashCodes = new HashSet<List<Integer>>();
+	// Cache whether a permutation is invalid (null) vs has been added to rotations
+	private Map<List<Integer>,Rotation> evaluatedPermutations = new HashMap<>();
 
 	private RotationGroup rotations = new RotationGroup();
 
@@ -95,9 +99,14 @@ public class RotationSolver implements QuatSymmetrySolver {
 
 		List<Double> angles = getAngles();
 
-		 for (int i = 0; i < sphereCount; i++) {
+		for (int i = 0; i < sphereCount; i++) {
+			// Sampled orientation
+			//TODO The SphereSampler samples 4D orientation space. We really
+			// only need to sample 3D unit vectors, since we use limited
+			// angles. -SB
 			SphereSampler.getAxisAngle(i, sphereAngle);
 
+			// Each valid rotation angle
 			for (double angle : angles) {
 				// apply rotation
 				sphereAngle.angle = angle;
@@ -113,14 +122,14 @@ public class RotationSolver implements QuatSymmetrySolver {
 				List<Integer> permutation = getPermutation();
 	//              System.out.println("Rotation Solver: permutation: " + i + ": " + permutation);
 
-				boolean isValidPermuation = isValidPermutation(permutation);
-				if (! isValidPermuation) {
-					continue;
+				// check if novel
+				if ( evaluatedPermutations.containsKey(permutation)) {
+					continue; //either invalid or already added
 				}
 
-				boolean newPermutation = evaluatePermutation(permutation);
-				if (newPermutation) {
-					completeRotationGroup();
+				Rotation newPermutation = isValidPermutation(permutation);
+				if (newPermutation != null) {
+					completeRotationGroup(newPermutation);
 				}
 
 				// check if all symmetry operations have been found.
@@ -131,33 +140,84 @@ public class RotationSolver implements QuatSymmetrySolver {
 		}
 	}
 
-	private void completeRotationGroup() {
+	/**
+	 * Combine current rotations to make all possible permutations.
+	 * If these are all valid, add them to the rotations
+	 * @param additionalRots Additional rotations we are considering adding to this.rotations
+	 * @return whether the rotations were valid and added
+	 */
+	private boolean completeRotationGroup(Rotation... additionalRots) {
 		PermutationGroup g = new PermutationGroup();
-		for (int i = 0; i < rotations.getOrder(); i++) {
-			Rotation s = rotations.getRotation(i);
+		for (Rotation s : rotations) {
 			g.addPermutation(s.getPermutation());
 		}
+		for( Rotation s : additionalRots) {
+			g.addPermutation(s.getPermutation());
+			// inputs should not have been added already
+			assert evaluatedPermutations.get(s.getPermutation()) == null;
+		}
 		g.completeGroup();
 
 		// the group is complete, nothing to do
-		if (g.getOrder() == rotations.getOrder()) {
-			return;
+		if (g.getOrder() == rotations.getOrder()+additionalRots.length) {
+			for( Rotation s : additionalRots) {
+				addRotation(s);
+			}
+			return true;
 		}
 
 		// try to complete the group
-		for (int i = 0; i < g.getOrder(); i++) {
-			List<Integer> permutation = g.getPermutation(i);
-
-			boolean isValidPermutation = isValidPermutation(permutation);
-			if (isValidPermutation) {
+		List<Rotation> newRots = new ArrayList<>(g.getOrder());
+		// First, quick check for whether they're allowed
+		for (List<Integer> permutation : g) {
+			if( evaluatedPermutations.containsKey(permutation)) {
+				Rotation rot = evaluatedPermutations.get(permutation);
+				if( rot == null ) {
+					return false;
+				}
+			} else {
+				if( ! isAllowedPermutation(permutation)) {
+					return false;
+				}
+			}
+		}
+		// Slower check including the superpositions
+		for (List<Integer> permutation : g) {
+			Rotation rot;
+			if( evaluatedPermutations.containsKey(permutation)) {
+				rot = evaluatedPermutations.get(permutation);
+			} else {
+				rot = isValidPermutation(permutation);
+			}
 
-					// perform permutation of subunits
-				evaluatePermutation(permutation);
+			if( rot == null ) {
+				// if any induced rotation is invalid, abort
+				return false;
+			}
+			if(!evaluatedPermutations.containsKey(permutation)){ //novel
+				newRots.add(rot);
 			}
 		}
+		// Add rotations
+		for( Rotation rot : newRots) {
+			addRotation(rot);
+		}
+		return true;
 	}
 
-	private boolean evaluatePermutation(List<Integer> permutation) {
+	private void addRotation(Rotation rot) {
+		evaluatedPermutations.put(rot.getPermutation(),rot);
+		rotations.addRotation(rot);
+	}
+
+	/**
+	 * Superimpose subunits based on the given permutation. Then check whether
+	 * the superposition passes RMSD thresholds and create a Rotation to
+	 * represent it if so.
+	 * @param permutation A list specifying which subunits should be aligned by the current transformation
+	 * @return A Rotation representing the permutation, or null if the superposition did not meet thresholds.
+	 */
+	private Rotation superimposePermutation(List<Integer> permutation) {
 		// permutate subunits
 		for (int j = 0, n = subunits.getSubunitCount(); j < n; j++) {
 			transformedCoords[j].set(originalCoords[permutation.get(j)]);
@@ -176,17 +236,20 @@ public class RotationSolver implements QuatSymmetrySolver {
 			// evaluate superposition of CA traces
 			QuatSymmetryScores scores = QuatSuperpositionScorer.calcScores(subunits, transformation, permutation);
 			if (scores.getRmsd() < 0.0 || scores.getRmsd() > parameters.getRmsdThreshold()) {
-				return false;
+				return null;
 			}
 
 			scores.setRmsdCenters(subunitRmsd);
 			Rotation symmetryOperation = createSymmetryOperation(permutation, transformation, axisAngle, fold, scores);
-			rotations.addRotation(symmetryOperation);
-			return true;
+			return symmetryOperation;
 		}
-		return false;
+		return null;
 	}
 
+	/**
+	 * Get valid rotation angles given the number of subunits
+	 * @return The rotation angle corresponding to each fold of {@link Subunits#getFolds()}
+	 */
 	private List<Double> getAngles() {
 		int n = subunits.getSubunitCount();
 		// for spherical symmetric cases, n cannot be higher than 60
@@ -210,44 +273,53 @@ public class RotationSolver implements QuatSymmetrySolver {
 		return m.getSymmetryClass(0.05) == MomentsOfInertia.SymmetryClass.SYMMETRIC;
 	}
 
-	private boolean isValidPermutation(List<Integer> permutation) {
-		 if (permutation.size() == 0) {
-			 return false;
-		 }
+	/**
+	 * Checks if a particular permutation is allowed and superimposes well.
+	 * Caches results.
+	 * @param permutation
+	 * @return null if invalid, or a rotation if valid
+	 */
+	private Rotation isValidPermutation(List<Integer> permutation) {
+		if (permutation.size() == 0) {
+			return null;
+		}
 
-		 // if this permutation is a duplicate, return false
-		if (hashCodes.contains(permutation)) {
-			return false;
+		// cached value
+		if (evaluatedPermutations.containsKey(permutation)) {
+			return evaluatedPermutations.get(permutation);
 		}
 
 		// check if permutation is allowed
 		if (! isAllowedPermutation(permutation)) {
-			return false;
-		}
-
-		// get fold and make sure there is only one E (fold=1) permutation
-		int fold = PermutationGroup.getOrder(permutation);
-		if (rotations.getOrder() > 1 && fold == 1) {
-			return false;
-		}
-
-		if (fold == 0 || subunits.getSubunitCount() % fold != 0) {
-			return false;
+			evaluatedPermutations.put(permutation, null);
+			return null;
 		}
 
-		// if this permutation is a duplicate, returns false
-		return hashCodes.add(permutation);
+		// check if superimposes
+		Rotation rot = superimposePermutation(permutation);
+		return rot;
 	}
 
+	/**
+	 * The permutation must map all subunits onto an equivalent subunit
+	 * and no subunit onto itself
+	 * @param permutation
+	 * @return
+	 */
 	private boolean isAllowedPermutation(List<Integer> permutation) {
 		List<Integer> seqClusterId = subunits.getSequenceClusterIds();
+		int selfaligned = 0;
 		for (int i = 0; i < permutation.size(); i++) {
 			int j = permutation.get(i);
-			if (seqClusterId.get(i) != seqClusterId.get(j)) {
+			if ( seqClusterId.get(i) != seqClusterId.get(j)) {
 				return false;
 			}
+			if(i == j ) {
+				selfaligned++;
+			}
 		}
-		return true;
+		// either identity (all self aligned) or all unique
+		return selfaligned == 0 || selfaligned == permutation.size();
 	}
 	/**
 	 * Adds translational component to rotation matrix
@@ -260,7 +332,7 @@ public class RotationSolver implements QuatSymmetrySolver {
 		rotation.mul(rotation, centroidInverse);
 	}
 
-	private Rotation createSymmetryOperation(List<Integer> permutation, Matrix4d transformation, AxisAngle4d axisAngle, int fold, QuatSymmetryScores scores) {
+	private static Rotation createSymmetryOperation(List<Integer> permutation, Matrix4d transformation, AxisAngle4d axisAngle, int fold, QuatSymmetryScores scores) {
 		Rotation s = new Rotation();
 		s.setPermutation(new ArrayList<Integer>(permutation));
 		s.setTransformation(new Matrix4d(transformation));
@@ -299,6 +371,11 @@ public class RotationSolver implements QuatSymmetrySolver {
 		return distanceThreshold;
 	}
 
+	/**
+	 * Compare this.transformedCoords with the original coords. For each
+	 * subunit, return the transformed subunit with the closest position.
+	 * @return A list mapping each subunit to the closest transformed subunit
+	 */
 	private List<Integer> getPermutation() {
 		List<Integer> permutation = new ArrayList<Integer>(transformedCoords.length);
 		double sum = 0.0f;
diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/Subunits.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/Subunits.java
index 6e980be..c639cae 100644
--- a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/Subunits.java
+++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/Subunits.java
@@ -65,6 +65,12 @@ public class Subunits {
 	 * @param modelNumbers Model number for the subunit
 	 */
 	public Subunits(List<Point3d[]> caCoords, List<Integer> sequenceClusterIds, List<Boolean> pseudoStoichiometry, List<Double> minSequenceIdentity, List<Double> maxSequenceIdentity, List<Integer> folds, List<String> chainIds, List<Integer> modelNumbers) {
+		
+		for (int i=0; i<caCoords.size(); i++) {
+			if (caCoords.get(i).length==0) 
+				throw new IllegalArgumentException("0-length coordinate array in subunit coordinates with index " +i+". Can't calculate quaternary symmetry for empty coordinates.");
+		}
+		
 		this.caCoords = caCoords;
 		this.sequenceClusterIds = sequenceClusterIds;
 		this.pseudoStoichiometry = pseudoStoichiometry;
diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/geometry/IcosahedralSampler.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/geometry/IcosahedralSampler.java
index 08803c5..650f2d6 100644
--- a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/geometry/IcosahedralSampler.java
+++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/geometry/IcosahedralSampler.java
@@ -24,9 +24,12 @@ import javax.vecmath.AxisAngle4d;
 import javax.vecmath.Quat4d;
 
 /**
- *
+ * Represents an even coverage of quaternion space by 60 points. This grid is
+ * defined by the vertices of one half of a hexacosichoron (600-cell). It
+ * approximates all possible orientations to within 44.48 degrees.
  * @author Peter
  */
+// This would be better named HexacosichoronSampler, since it's sampling 4D space. -SB
 public final class IcosahedralSampler {
 	private static Quat4d quat = new Quat4d();
 
@@ -39,7 +42,7 @@ public final class IcosahedralSampler {
 	}
 
 	public static Quat4d getQuat4d(int index) {
-		Quat4d q = new Quat4d(orientations[index]);
+		Quat4d q = new Quat4d(orientations[index]); //ignores 5th element
 		return q;
 	}
 
@@ -48,10 +51,12 @@ public final class IcosahedralSampler {
 		axisAngle.set(quat);
 	}
 
-//	# Orientation set c600v, number = 60, radius = 44.48 degrees
-//	# $Id: c600v.quat 6102 2006-02-21 19:45:40Z ckarney $
-//	# For more information, eee http://charles.karney.info/orientation/
-//	format quaternion
+	//	# Orientation set c600v, number = 60, radius = 44.48 degrees
+	//	# $Id: c600v.quat 6102 2006-02-21 19:45:40Z ckarney $
+	//	# For more information, eee http://charles.karney.info/orientation/
+	//	format quaternion
+	// The fifth column gives a weighting factor. Since the 600-cell is regular, all
+	// orientations cover an equal fraction of orientation space and have equal weight.
 	private static double[][] orientations = {
 	{1.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 1.000000f},
 	{0.000000000f, 1.000000000f, 0.000000000f, 0.000000000f, 1.000000f},
diff --git a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/geometry/SphereSampler.java b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/geometry/SphereSampler.java
index d6253eb..032ca4b 100644
--- a/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/geometry/SphereSampler.java
+++ b/biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/geometry/SphereSampler.java
@@ -25,7 +25,11 @@ import java.util.ArrayList;
 import java.util.List;
 
 
-// Generate the permutations and sign changes for a Triple.
+/**
+ * Generate the permutations and sign changes for a Triple.
+ * For instance, (a,0,0) becomes (±a,0,0),(0,±a,0),(0,0,±a). In the general
+ * case 48 tuples are returned.
+ */
 class Permute {
 
 	private List<Point3i> triples = new ArrayList<Point3i>();
@@ -38,6 +42,7 @@ class Permute {
 		triples.add(tmp);
 		int n = 1;
 
+		// Unpermuted ±x,±y,±z
 		if (t.x != 0) {
 			for (int i = 0; i < n; ++i) {
 				Tuple3i m = triples.get(i);
@@ -66,6 +71,7 @@ class Permute {
 			return;
 		}
 
+		// At least one differing value
 		for (int i = 0; i < n; ++i) {
 			Point3i m = triples.get(i);
 			triples.add(new Point3i(m.y, m.z, m.x));
@@ -77,6 +83,7 @@ class Permute {
 			return;
 		}
 
+		// At least two differing values
 		for (int i = 0; i < n; ++i) {
 			Point3i m = triples.get(i);
 			triples.add(new Point3i(m.y, m.x, m.z));
@@ -94,7 +101,10 @@ class Permute {
 };
 
 /**
- *
+ * Sample possible orientations. An orientation can be represented as a
+ * quaternion or as a rotation axis and angle. The orientations are somewhat
+ * evenly distributed over orientation space, but the current implementation
+ * is not suited for statistically uniform sampling.
  * @author Peter
  */
 public final class SphereSampler {
@@ -125,16 +135,20 @@ public final class SphereSampler {
 
 
 
+	// Approximate spacing between grid points
 	private static final double delta = 0.15846;
+	// Amount of distortion towards grid corners to account for the projection back to a 4-sphere
 	private static final double sigma = 0.00;
-	private static final int ntot = 7416;
-	private static final int ncell = 309;
-	private static final int nent = 18;
+	private static final int ntot = 7416; // total number of grid points over the 24 cells
+	private static final int ncell = 309; // number of grid points per cell
+	private static final int nent = 18; // number of distinct grid points (with k>=l>=m)
 
+	// Why not (5,5,3) and (5,5,5)? Would they overlap with other cells? -SB
 	private static final int[] k = { 0, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5};
 	private static final int[] l = { 0, 1, 0, 2, 2, 1, 3, 3, 0, 2, 2, 4, 4, 4, 1, 3, 3, 5};
 	private static final int[] m = { 0, 1, 0, 0, 2, 1, 1, 3, 0, 0, 2, 0, 2, 4, 1, 1, 3, 1};
 
+	// number of permutations to expect for each (k,l,m) tuple
 	private static final int[] mult = { 1, 8, 6, 12, 8, 24, 24, 8, 6, 24, 24, 12, 24, 8, 24, 48, 24, 24};
 
 	static
@@ -142,15 +156,29 @@ public final class SphereSampler {
 
 		List<Quat4d> myorientations = new ArrayList<Quat4d>();
 
+		// 60 equally spaced grid points covering all quaternions
+		// This could be removed, since the 48-cell bcc lattice has lower angle error -SB
 		for (int i = 0; i < IcosahedralSampler.getSphereCount(); i++) {
 			myorientations.add(IcosahedralSampler.getQuat4d(i));
 		}
+
+		// SB's interpretation of this code:
+		// Directly form a 4D grid over the 4-sphere of orientations. Start by
+		// making a 3D body-centered lattice with w=1. Then use the hypercube symmetries
+		// to extend this grid over the whole surface.
+		// The permuted (k,l,m) values together make a diagonal grid out to ±(5,5,5).
+		// The point spacing is distorted by the pind() function so that the
+		// projection of the points back to the 4-sphere will be more even.
+		
+		// This is the c48u309 lattice from Karney 2006, with a max error of 10.07
+		// degrees.
+		
 		List<Quat4d> grid = new ArrayList<Quat4d>();
 		int ncell1 = 0;
-		for (int n = 0; n < nent; ++n) {
+		for (int n = 0; n < nent; ++n) { // for each tuple (k,l,m) above
 			Permute p = new Permute(new Point3i(k[n], l[n], m[n]));
 			assert (mult[n] == p.size());
-			for (int i = 0; i < mult[n]; ++i) {
+			for (int i = 0; i < mult[n]; ++i) { // for each permutation
 				Point3i t = p.get(i);
 				grid.add(new Quat4d(1.0, pind(0.5 * t.x, delta, sigma), pind(
 						0.5 * t.y, delta, sigma), pind(0.5 * t.z, delta, sigma)));
@@ -158,6 +186,7 @@ public final class SphereSampler {
 			ncell1 += mult[n];
 		}
 		assert (ncell1 == ncell);
+		// Apply symmetry operations to distribute grid over all values of w
 		int nc = grid.size();
 		assert (nc == ncell);
 		for (int n = 1; n < 24; ++n) {
@@ -168,6 +197,7 @@ public final class SphereSampler {
 				qs.mul(q, grid.get(i));
 				grid.add(qs);
 				//	s.add(times(q, s.getOrientation(i)), s.getWeight(i)); // this data set has no weights
+				// Actually, it does have weights but we don't care since we don't need uniform sampling. -SB
 			}
 		}
 		assert (grid.size() == ntot);
@@ -196,6 +226,10 @@ public final class SphereSampler {
 		axisAngle.set(orientations.get(index));
 	}
 
+	/**
+	 * @param index Index between 0 and {@link #getSphereCount()}-1
+	 * @param axisAngle (Output) modified to contain the index-th sampled orientation
+	 */
 	public static void getAxisAngle(int index, AxisAngle4d axisAngle) {
 
 		axisAngle.set(orientations.get(index));
diff --git a/biojava-survival/pom.xml b/biojava-survival/pom.xml
index d7c072c..e6c0294 100644
--- a/biojava-survival/pom.xml
+++ b/biojava-survival/pom.xml
@@ -4,7 +4,7 @@
     <parent>
         <groupId>org.biojava</groupId>
         <artifactId>biojava</artifactId>
-        <version>4.2.5</version>
+        <version>4.2.7</version>
     </parent>
 
     <artifactId>biojava-survival</artifactId>
diff --git a/biojava-ws/pom.xml b/biojava-ws/pom.xml
index 490d79c..0f2e83e 100644
--- a/biojava-ws/pom.xml
+++ b/biojava-ws/pom.xml
@@ -3,7 +3,7 @@
 	<parent>
 		<artifactId>biojava</artifactId>
 		<groupId>org.biojava</groupId>
-		<version>4.2.5</version>
+		<version>4.2.7</version>
 	</parent>	
 	<artifactId>biojava-ws</artifactId>
 	<name>biojava-ws</name>
@@ -19,7 +19,7 @@
 		<dependency>
 			<groupId>org.biojava</groupId>
 			<artifactId>biojava-core</artifactId>
-			<version>4.2.5</version>
+			<version>4.2.7</version>
 			<scope>compile</scope>
 		</dependency>
 		<dependency>
diff --git a/biojava-ws/src/main/java/org/biojava/nbio/ws/hmmer/RemoteHmmerScan.java b/biojava-ws/src/main/java/org/biojava/nbio/ws/hmmer/RemoteHmmerScan.java
index 4e83b61..586aad7 100644
--- a/biojava-ws/src/main/java/org/biojava/nbio/ws/hmmer/RemoteHmmerScan.java
+++ b/biojava-ws/src/main/java/org/biojava/nbio/ws/hmmer/RemoteHmmerScan.java
@@ -23,6 +23,8 @@ package org.biojava.nbio.ws.hmmer;
 import net.sf.json.JSONArray;
 import net.sf.json.JSONObject;
 import org.biojava.nbio.core.sequence.ProteinSequence;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.io.*;
 import java.net.HttpURLConnection;
@@ -31,14 +33,17 @@ import java.util.SortedSet;
 import java.util.TreeSet;
 
 
-/** Makes remote calls to the HMMER web service at the EBI web site and returns Pfam domain annotations for an input protein sequence.
+/** 
+ * Makes remote calls to the HMMER web service at the EBI web site and returns Pfam domain annotations for an input protein sequence.
  *
  * @author Andreas Prlic
  * @since 3.0.3
  */
 public class RemoteHmmerScan implements HmmerScan {
 
-	public static String HMMER_SERVICE = "http://www.ebi.ac.uk/Tools/hmmer/search/hmmscan";
+	private static final Logger LOGGER = LoggerFactory.getLogger(RemoteHmmerScan.class);
+	
+	public static final String HMMER_SERVICE = "http://www.ebi.ac.uk/Tools/hmmer/search/hmmscan";
 
 	public RemoteHmmerScan(){
 
@@ -54,7 +59,8 @@ public class RemoteHmmerScan implements HmmerScan {
 
 	}
 
-	/** Scans a protein sequence for Pfam profile matches.
+	/** 
+	 * Scans a protein sequence for Pfam profile matches.
 	 *
 	 * @param sequence
 	 * @param serviceLocation
@@ -68,7 +74,7 @@ public class RemoteHmmerScan implements HmmerScan {
 		postContent.append("hmmdb=pfam");
 
 
-		// by default hmmscan runs with the HMMER3 cut_ga parameter enabled, the "gathering freshold", which depends on
+		// by default hmmscan runs with the HMMER3 cut_ga parameter enabled, the "gathering threshold", which depends on
 		// the cutoffs defined in the underlying HMM files.
 		// to request a different cutoff by e-value this could be enabled:
 		//postContent.append("&E=1");
@@ -86,7 +92,7 @@ public class RemoteHmmerScan implements HmmerScan {
 		connection.setRequestMethod("POST");
 		connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
 
-		connection.setRequestProperty("Accept:","application/json");
+		connection.setRequestProperty("Accept","application/json");
 
 		connection.setRequestProperty("Content-Length", "" +
 				Integer.toString(postContent.toString().getBytes().length));
@@ -99,13 +105,12 @@ public class RemoteHmmerScan implements HmmerScan {
 		wr.close ();
 
 
-//		//Now get the redirect URL
+		//Now get the redirect URL
 		URL respUrl = new URL( connection.getHeaderField( "Location" ));
 
 		int responseCode = connection.getResponseCode();
 		if ( responseCode == 500){
-			System.err.println("something went wrong!" + serviceLocation);
-			System.err.println(connection.getResponseMessage());
+			LOGGER.warn("Got 500 response code for URL {}. Response message: {}.", serviceLocation, connection.getResponseMessage());	
 		}
 
 		HttpURLConnection connection2 = (HttpURLConnection) respUrl.openConnection();
@@ -122,7 +127,6 @@ public class RemoteHmmerScan implements HmmerScan {
 
 		StringBuffer result = new StringBuffer();
 		while ((inputLine = in.readLine()) != null) {
-			//System.out.println(inputLine);
 			result.append(inputLine);
 		}
 
@@ -141,7 +145,6 @@ public class RemoteHmmerScan implements HmmerScan {
 
 			for(int i =0 ; i < hits.size() ; i++){
 				JSONObject hit = hits.getJSONObject(i);
-				//System.out.println("hit: "+ hit);
 
 				HmmerResult hmmResult = new HmmerResult();
 
@@ -170,13 +173,8 @@ public class RemoteHmmerScan implements HmmerScan {
 				SortedSet<HmmerDomain> domains = new TreeSet<HmmerDomain>();
 				for ( int j= 0 ; j < hmmdomains.size() ; j++){
 					JSONObject d = hmmdomains.getJSONObject(j);
-					//System.out.println(d);
 					Integer is_included = getInteger(d.get("is_included"));
 					if ( is_included == 0) {
-//						System.out.println("  excluding: " + d.get("alihmmdesc") + " " + d.get("alihmmname") + " " +
-//								hit.get("evalue") + " " +
-//								d.get("alisqfrom") + " " +
-//								d.get("alisqto"));
 						continue;
 					}
 
@@ -184,21 +182,12 @@ public class RemoteHmmerScan implements HmmerScan {
 					// this filters out multiple hits to the same clan
 					Integer outcompeted = getInteger(d.get("outcompeted"));
 					if ( outcompeted != null && outcompeted == 1) {
-//						System.out.println("  outcompeted: " + d.get("alihmmdesc") + " " + d.get("alihmmname")+ " " +
-//								hit.get("evalue") + " " +
-//								d.get("alisqfrom") + " " +
-//								d.get("alisqto")
-//						);
 						continue;
 					}
 
 					Integer significant = getInteger(d.get("significant"));
 
 					if (  significant != 1) {
-//						System.out.println("  not significant: " + d.get("alihmmdesc") + " " + d.get("alihmmname")+ " " +
-//								hit.get("evalue") + " " +
-//								d.get("alisqfrom") + " " +
-//								d.get("alisqto"));
 						continue;
 					}
 
@@ -224,8 +213,8 @@ public class RemoteHmmerScan implements HmmerScan {
 
 				results.add(hmmResult);
 			}
-		} catch (Exception e){
-			e.printStackTrace();
+		} catch (NumberFormatException e){
+			LOGGER.warn("Could not parse number in Hmmer web service json response: {}", e.getMessage());
 		}
 
 		return results;
diff --git a/pom.xml b/pom.xml
index e1daf1f..6513c4c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -12,7 +12,7 @@
 	<groupId>org.biojava</groupId>
 	<artifactId>biojava</artifactId>
 	<packaging>pom</packaging>
-	<version>4.2.5</version>
+	<version>4.2.7</version>
 	<name>biojava</name>
 	<description>BioJava is an open-source project dedicated to providing a Java framework for processing biological
         data. It provides analytical and statistical routines, parsers for common file formats and allows the
@@ -44,7 +44,7 @@
 		<developerConnection>scm:git:git at github.com:biojava/biojava.git</developerConnection>
 		<url>https://github.com/biojava/biojava</url>
 
-		<tag>biojava-4.2.5</tag>
+		<tag>biojava-4.2.7</tag>
 	</scm>
 
 	<!-- This section required for release at Maven Central. For a full list 

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/biojava4-live.git



More information about the debian-med-commit mailing list