Merge branch 'develop' into Release_2_9_0b1_Branch
authorJim Procter <jprocter@issues.jalview.org>
Tue, 22 Sep 2015 14:19:51 +0000 (15:19 +0100)
committerJim Procter <jprocter@issues.jalview.org>
Tue, 22 Sep 2015 14:19:51 +0000 (15:19 +0100)
JAL-1894 update next-release branch for build system

32 files changed:
1  2 
build.xml
doc/UnitTesting.html
examples/applets.html
examples/javascript/jalview.js
src/jalview/analysis/AlignmentUtils.java
src/jalview/appletgui/AlignFrame.java
src/jalview/appletgui/CutAndPasteTransfer.java
src/jalview/appletgui/SeqPanel.java
src/jalview/appletgui/SplitFrame.java
src/jalview/bin/Cache.java
src/jalview/bin/JalviewLite.java
src/jalview/commands/EditCommand.java
src/jalview/datamodel/AlignedCodonFrame.java
src/jalview/datamodel/Mapping.java
src/jalview/gui/AlignFrame.java
src/jalview/gui/AlignViewport.java
src/jalview/gui/Jalview2XML.java
src/jalview/gui/SeqPanel.java
src/jalview/io/BioJsHTMLOutput.java
src/jalview/io/FormatAdapter.java
src/jalview/io/JSONFile.java
src/jalview/javascript/MouseOverStructureListener.java
src/jalview/jbgui/GAlignFrame.java
src/jalview/json/binding/biojson/v1/SequencePojo.java
src/jalview/ws/DBRefFetcher.java
test/jalview/analysis/AlignmentUtilsTests.java
test/jalview/commands/EditCommandTest.java
test/jalview/datamodel/AlignedCodonFrameTest.java
test/jalview/datamodel/AlignedCodonIteratorTest.java
test/jalview/datamodel/AlignmentTest.java
test/jalview/io/Jalview2xmlTests.java
test/jalview/util/MappingUtilsTest.java

diff --combined build.xml
+++ b/build.xml
@@@ -1,7 -1,7 +1,7 @@@
  <?xml version="1.0"?>
  <!--
 - * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
 - * Copyright (C) $$Year-Rel$$ The Jalview Authors
 + * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
 + * Copyright (C) 2015 The Jalview Authors
   * 
   * This file is part of Jalview.
   * 
@@@ -19,7 -19,7 +19,7 @@@
  -->
  <project name="jalviewX" default="usage" basedir=".">
    <target name="help" depends="usage" />
-   <target name="usage">
+   <target name="usage" depends="init">
      <echo message="~~~Jalview Ant build.xml Usage~~~~" />
      <echo message="Targets include:" />
      <echo message="usage - default target, displays this message" />
@@@ -30,6 -30,8 +30,8 @@@
      <echo message="              this needs a keystore and key. See docs/building.html for more information." />
      <echo message="compileApplet - compiles all necessary files for Applet" />
      <echo message="makeApplet - compiles, then packages and obfuscates the Applet" />
+     <echo message="testng - run jalview's tests via testNG. Default group is '${testng-groups}'" />
+     <echo message="      you can specify particular test groups as a list via -Dtestng-groups=" />
      <echo message="See docs/building.html and the comments in build file for other targets." />
      <echo message="note: compile and makeApplet require the property java118.home to be set to point to a java 1.1.8 jdk." />
      <echo message="Useful -D flags: -Ddonotobfuscate will prevent applet obfuscation" />
          <include name="*.jar"/>
        </fileset> -->
        </path>
-       
      <!-- Jalview Version String displayed by application on startup and used to check for updates -->
      <property name="JALVIEW_VERSION" value="DEVELOPMENT" />
-       
      <property name="INSTALLATION" value="Source" />
-       
      <!-- 2.4 (VAMSAS)" -->
      <!-- Include debugging information in javac true or false -->
      <property name="javac.debug" value="true" />
      <!-- Key Password -->
      <property name="jalview.key.pass" value="alignmentisfun" />
  
+     <property name="testng-groups" value="Functional" />
      <!-- Don't change anything below here unless you know what you are doing! -->
      <!-- Url path for WebStart in JNLP file -->
      <property name="WebStartLocation" value="http://www.jalview.org/webstart" />
      <!-- Webstart Image - looked for in resources/images -->
-     <property name="WebStartImage" value="JalviewLogo_big.png"/>
+     <property name="WebStartImage" value="JalviewLogo_big.png" />
      <!-- J2SE version needed for webstart launch -->
      <!-- Anne's version needs 1.7 - should rebuild VARNA to java 1.6 for release -->
-     <property name="j2sev" value="1.7+"/>
-       <!-- Java Compilation settings - source and target javac version -->
-       <property name="javac.source" value="1.7"/>
-       <property name="javac.target" value="1.7"/>
-               
+     <property name="j2sev" value="1.7+" />
+     <!-- Java Compilation settings - source and target javac version -->
+     <property name="javac.source" value="1.7" />
+     <property name="javac.target" value="1.7" />
      <!-- Permissions for running Java applets and applications. -->
      <!-- Defaults are those suitable for deploying jalview webstart www.jalview.org -->
      <property name="application.codebase" value="*.jalview.org" />
      <property name="jsonSimple" value="json_simple-1.1.jar" />
      <property name="javaJson" value="java-json.jar" />
      <property name="jalviewLiteJar" value="jalviewApplet.jar" />
-       <property name="reportDir" value="test-reports" />
-       <property name="testDir" value="test" />
-       <property name="testOutputDir" value="tests" />
+     <property name="reportDir" value="test-reports" />
+     <property name="testDir" value="test" />
+     <property name="testOutputDir" value="tests" />
      <!-- switch to indicate if we should obfuscate jalviewLite -->
      <!-- <property name="donotobfuscate" value="true"/> -->
      <!-- switch to exclude associations from generated jnlp files -->
          <include name="**/*.jar" />
        </fileset>
        <fileset dir="${java.home}/lib">
-         <include name="plugin.jar"/>
+         <include name="plugin.jar" />
        </fileset>
        <fileset dir="appletlib">
          <!-- the JmolApplet includes the JmolApplet console and the application javac seems to always try and build all packages 
          <include name="${jmolJar}" />
          <include name="${varnaJar}" />
        </fileset>
+     </path>
+     <path id="test.classpath">
+       <pathelement path="${outputDir}" />
+       <path refid="build.classpath" />
      </path>
      <property name="source.dist.name" value="${basedir}/jalview-src.tar.gz" />
      <!-- The Location of the java 1.1.8 jdk -->
      <!-- <property name="applet.jre.tools" value="${java118.home}/lib/classes.zip" />
                -->
      <!-- jre for 1.4 version -->
-     <property name="applet.jre.tools" value="${java.home}/lib/rt.jar"/>
+     <property name="applet.jre.tools" value="${java.home}/lib/rt.jar" />
  
      <!-- the classpath for building the 1.1 applet -->
      <path id="jalviewlite.deps">
          <include name="lib/classes.zip" />
        </fileset>
        <fileset dir="${java.home}/lib">
-         <include name="plugin.jar"/>
+         <include name="plugin.jar" />
        </fileset>
        <pathelement location="appletlib/${jmolJar}" />
        <pathelement location="lib/${varnaJar}" />
        <pathelement location="lib/${jsoup}" />
        <pathelement location="lib/${jsonSimple}" />
        <pathelement location="lib/${javaJson}" />
-       
      </path>
      <!-- default location for outputting javadoc -->
-     <property name="javadocDir" value="${packageDir}/javadoc"/>
+     <property name="javadocDir" value="${packageDir}/javadoc" />
    </target>
  
  
      <tstamp prefix="build">
        <format property="date" pattern="dd MMMM yyyy" />
      </tstamp>
-     <exec executable="/usr/bin/git" outputproperty="git.commit"  failifexecutionfails="false">
-       <arg value="rev-parse"/>
-       <arg value="--short"/>
-       <arg value="HEAD"/>
+     <exec executable="/usr/bin/git" outputproperty="git.commit" failifexecutionfails="false">
+       <arg value="rev-parse" />
+       <arg value="--short" />
+       <arg value="HEAD" />
      </exec>
      <exec executable="/usr/bin/git" outputproperty="git.branch" failifexecutionfails="false">
-       <arg value="rev-parse"/>
-       <arg value="--abbrev-ref"/>
-       <arg value="HEAD"/>
+       <arg value="rev-parse" />
+       <arg value="--abbrev-ref" />
+       <arg value="HEAD" />
      </exec>
      <properties file="${outputDir}/.build_properties">
        <header>
            ---Jalview Build Details---
-         </header>     
+         </header>
        <property name="VERSION" value="${JALVIEW_VERSION}" />
        <property name="INSTALLATION" value="${INSTALLATION} git-commit:${git.commit} [${git.branch}]" />
        <property name="BUILD_DATE" value="${build.date}" />
  
    <target name="clean" depends="init">
      <!-- not efficient yet. -->
-     <delete dir="${outputDir}" includes="*,**/*"/>
+     <delete dir="${outputDir}" includes="*,**/*" />
    </target>
  
    <target name="distclean" depends="init, clean">
        <exclude name="com/stevesoft/**" />
      </javac>
    </target>
-   
-   
    <target name="testclean" depends="init">
-     <delete dir="${testOutputDir}" includes="*,**/*"/>
+     <delete dir="${testOutputDir}" includes="*,**/*" />
    </target>
-   
-   <target name="prepareTests" depends="init">
-       <mkdir dir="${testOutputDir}" />
-       <copy todir="${testOutputDir}">
-               <fileset dir=".">
-                       <include name="${docDir}/**/*.*" />
-                       <include name="${helpDir}/**/*.*" />
-                       <include name="${libDir}/*.jar" />
-               </fileset>
-               <fileset dir="${resourceDir}">
-                       <include name="**/*.*" />
-               </fileset>
-       </copy>
-   </target>  
-   
-   <target name="buildTests" depends="prepareTests">
-       <javac source="${javac.source}" target="${javac.target}" srcdir="${sourceDir}" destdir="${testOutputDir}"
-               debug="${javac.debug}" classpathref="build.classpath" includeantruntime="false" >               
-       </javac>
-       <javac source="${javac.source}" target="${javac.target}" srcdir="${testDir}" destdir="${testOutputDir}"
-               debug="${javac.debug}" classpathref="build.classpath" includeantruntime="false" >
-       </javac>
+   <target name="prepareTests" depends="init,testclean">
+     <mkdir dir="${testOutputDir}" />
    </target>
-   
-   <taskdef name="testng" classname="org.testng.TestNGAntTask" >           
-       <classpath location="utils/testnglibs/testng.jar" />
-   </taskdef>
-   
+   <target name="buildTests" depends="build,buildindices,prepareTests">
+     <javac source="${javac.source}" target="${javac.target}" srcdir="${testDir}" destdir="${testOutputDir}" debug="${javac.debug}" classpathref="test.classpath" includeantruntime="false">
+     </javac>
+   </target>
+   <taskdef resource="testngtasks" classpath="utils/testnglibs/testng.jar" />
    <target name="testng" depends="buildTests">
-       <testng classpathref="build.classpath" outputDir="${reportDir}"
-               haltOnFailure="false">
-               <classpath location="${testOutputDir}" />
-               <xmlfileset dir="utils" includes="jalview_testng.xml" />
-       </testng>
+     <testng outputDir="${reportDir}" haltOnFailure="false" groups="${testng-groups}" mode="testng"
+       verbose="2">
+       <classpath>
+         <pathelement location="${testOutputDir}" />
+         <path refid="test.classpath" />
+       </classpath>
+       <classfileset dir="${testOutputDir}" includes="**/*.class" />
+     </testng>
    </target>
-   
    <target name="buildindices" depends="init, prepare" unless="help.uptodate">
      <java classname="com.sun.java.help.search.Indexer" classpathref="build.classpath" fork="true" dir="${outputDir}/${helpDir}">
        <arg line="html" />
    <target name="makefulldist" depends="makedist">
      <copy todir="${packageDir}">
        <fileset dir="${resourceDir}/images">
-         <include name="${WebStartImage}"/>
+         <include name="${WebStartImage}" />
        </fileset>
      </copy>
  
        </fileset>
      </jar>
  
-     <mkdir dir="${packageDir}/JNLP-INF"/>
+     <mkdir dir="${packageDir}/JNLP-INF" />
      <antcall target="writejnlpf">
-       <param name="jnlpFile" value="${packageDir}/JNLP-INF/APPLICATION-TEMPLATE.JNLP"/>
+       <param name="jnlpFile" value="${packageDir}/JNLP-INF/APPLICATION-TEMPLATE.JNLP" />
        <param name="inih" value="*" />
-       <param name="maxh" value="*"/>
+       <param name="maxh" value="*" />
      </antcall>
  
      <jar destfile="${packageDir}/jalview_jnlp_vm.jar" index="true">
      </jar>
  
      <antcall target="writejnlpf">
-       <param name="jnlpFile" value="${packageDir}/jalview.jnlp"/>
+       <param name="jnlpFile" value="${packageDir}/jalview.jnlp" />
        <param name="inih" value="10M" />
-       <param name="maxh" value="256M"/>
+       <param name="maxh" value="256M" />
      </antcall>
  
      <antcall target="writejnlpf">
-       <param name="jnlpFile" value="${packageDir}/jalview_1G.jnlp"/>
+       <param name="jnlpFile" value="${packageDir}/jalview_1G.jnlp" />
        <param name="inih" value="128M" />
-       <param name="maxh" value="512M"/>
+       <param name="maxh" value="512M" />
      </antcall>
  
      <antcall target="writejnlpf">
-       <param name="jnlpFile" value="${packageDir}/jalview_2G.jnlp"/>
+       <param name="jnlpFile" value="${packageDir}/jalview_2G.jnlp" />
        <param name="inih" value="256M" />
-       <param name="maxh" value="1024M"/>
+       <param name="maxh" value="1024M" />
      </antcall>
  
      <!-- finally, need to postprocess to add in associations at end of 'information' element 
              <include name="*.jar" />
              <include name="*_*.jar" />
              <exclude name="jalview.jar" />
-               <exclude name="*jnilib.jar"/>
+             <exclude name="*jnilib.jar" />
            </fileset>
-               </resources>
+         </resources>
          <resources os="Mac OS X">
            <property name="jalview.version" value="${JALVIEW_VERSION}" />
-               <fileset dir="${packageDir}">
-                       <include name="*quaqua*.jnilib.jar"/>
-               </fileset>
+           <fileset dir="${packageDir}">
+             <include name="*quaqua*.jnilib.jar" />
+           </fileset>
          </resources>
-               
          <application_desc main_class="jalview.bin.Jalview">
          </application_desc>
          <security>
        </jnlp>
      </presetdef>
  
-     <jnlpf toFile="${jnlpFile}"/>
+     <jnlpf toFile="${jnlpFile}" />
  
    </target>
  
    <target name="-dofakejnlpfileassoc" depends="-generatejnlpf" if="nojnlpfileassocs">
-     <echo message="Not adding JNLP File Associations"/>
+     <echo message="Not adding JNLP File Associations" />
    </target>
  
    <target name="-dojnlpfileassoc" depends="-generatejnlpf" unless="nojnlpfileassocs">
          <association mime-type="application-x/ext-file" extensions="jvp"/>
        </information>]]></replacevalue>
    </replace>
-   <echo message="Added file associations to JNLP file"/>
+   <echo message="Added file associations to JNLP file" />
  </target>
  <target name="writejnlpf" depends="-dojnlpfileassoc,-dofakejnlpfileassoc">
  </target>
    <!-- clean dir if it already existed -->
    <delete>
      <fileset dir="${packageDir}">
-       <include name="*.jar"/>
+       <include name="*.jar" />
      </fileset>
    </delete>
    <jar destfile="${packageDir}/${outputJar}" index="true">
  
  
  <!-- Compile, package and obfuscate Jalview Applet -->
- <target name="makeApplet" depends="obfuscate" description="assemble the final jalviewLite applet jar with or without obfuscation"/>
+ <target name="makeApplet" depends="obfuscate" description="assemble the final jalviewLite applet jar with or without obfuscation" />
  
  <target name="compileApplet" depends="init,clean">
    <mkdir dir="${outputDir}" />
-   <javac source="${javac.source}" target="${javac.target}" srcdir="${sourceDir}" destdir="${outputDir}" debug="${javac.debug}" 
-                       classpathref="jalviewlite.deps" includes="jalview/appletgui/**"
-                       excludes="ext/**,gui/**,jbgui/**,MCview/**,org/**,vamsas/**,jalview/ext/rbvi/**,jalview/ext/paradise/**" />
+   <javac source="${javac.source}" target="${javac.target}" srcdir="${sourceDir}" destdir="${outputDir}" debug="${javac.debug}" classpathref="jalviewlite.deps" includes="jalview/appletgui/**" excludes="ext/**,gui/**,jbgui/**,MCview/**,org/**,vamsas/**,jalview/ext/rbvi/**,jalview/ext/paradise/**" />
  </target>
  
  <target name="packageApplet" depends="compileApplet, buildPropertiesFile">
    <copy file="${resourceDir}/images/link.gif" toFile="${outputDir}/images/link.gif" />
    <copy todir="${outputDir}/lang">
      <fileset dir="${resourceDir}/lang">
-       <include name="**.*"/>
+       <include name="**.*" />
      </fileset>
    </copy>
    <jar destfile="in.jar" index="true">
      <injar file="in.jar" />
      <outjar file="${jalviewLiteJar}" />
      <libraryjar refid="obfuscateDeps.path" />
-     <dontwarn/>
+     <dontwarn />
      <keep access="public" type="class" name="jalview.bin.JalviewLite">
        <field access="public" />
        <method access="public" />
        <include name="jalview-jalopy.xml" />
        <include name="JalviewApplet.jpx" />
        <include name="JalviewX.jpx" />
-       <include name="nbbuild.xml"/>
-       <include name="nbproject/genfiles.properties"/>
-       <include name="nbproject/project.properties"/>
-       <include name="nbproject/project.xml"/>
+       <include name="nbbuild.xml" />
+       <include name="nbproject/genfiles.properties" />
+       <include name="nbproject/project.properties" />
+       <include name="nbproject/project.xml" />
        <include name="${sourceDir}/*.java" />
        <include name="${sourceDir}/**/*.java" />
        <include name="${sourceDir}/**/*.cdr" />
  <target name="pubapplet" description="installs the jalviewLite applet and dependent jars into an applet examples directory built under ${outputDir}" depends="makeApplet">
    <copy todir="${packageDir}/examples">
      <fileset dir="examples">
-       <include name="**/*"/>
-       <include name="javascript/*"/>
-       <include name="jmol/*"/>
+       <include name="**/*" />
+       <include name="javascript/*" />
+       <include name="jmol/*" />
      </fileset>
      <fileset dir=".">
        <include name="${jalviewLiteJar}" />
      </fileset>
      <fileset dir="appletlib">
-       <include name="**/*"/>
+       <include name="**/*" />
      </fileset>
    </copy>
-   <jar update="true" index="true" jarfile="${packageDir}/examples/${jalviewLiteJar}"/>
-   <jar update="true" index="true" jarfile="${packageDir}/examples/${javaJson}"/>
-   <jar update="true" index="true" jarfile="${packageDir}/examples/${jsonSimple}"/>
+   <jar update="true" index="true" jarfile="${packageDir}/examples/${jalviewLiteJar}" />
+   <jar update="true" index="true" jarfile="${packageDir}/examples/${javaJson}" />
+   <jar update="true" index="true" jarfile="${packageDir}/examples/${jsonSimple}" />
    <jar update="true" index="true" jarfile="${packageDir}/examples/${jmolJar}">
      <manifest>
-       <attribute name="Application-Name" value="Jmol (bundled with JalviewLite)"/>
+       <attribute name="Application-Name" value="Jmol (bundled with JalviewLite)" />
        <!--          <attribute name="Permissions" value="sandbox" /> -->
        <!--<attribute name="Trusted-Lib" value="true" /> -->
-       <attribute name="Codebase" value="${applet.codebase}"/>
-       <attribute name="Caller-Allowable-Codebase" value="${applet.caller-codebase}"/>
+       <attribute name="Codebase" value="${applet.codebase}" />
+       <attribute name="Caller-Allowable-Codebase" value="${applet.caller-codebase}" />
      </manifest>
    </jar>
    <signjar sigalg="SHA1WithRSA" storepass="${jalview.keystore.pass}" keypass="${jalview.key.pass}" keystore="${jalview.keystore}" alias="${jalview.key}" lazy="false" verbose="false">
    </presetdef>
    <!-- create differently privileged artefacts -->
    <copy file="${packageDir}/examples/${jalviewLiteJar}" tofile="${packageDir}/examples/u_${jalviewLiteJar}" />
-   <copy file="${packageDir}/examples/${jmolJar}" tofile="${packageDir}/examples/u_${jmolJar}" overwrite="true"/>
-   <copy file="${packageDir}/examples/${javaJson}" tofile="${packageDir}/examples/u_${javaJson}" overwrite="true"/>
-   <copy file="${packageDir}/examples/${jsonSimple}" tofile="${packageDir}/examples/u_${jsonSimple}" overwrite="true"/>
+   <copy file="${packageDir}/examples/${jmolJar}" tofile="${packageDir}/examples/u_${jmolJar}" overwrite="true" />
+   <copy file="${packageDir}/examples/${javaJson}" tofile="${packageDir}/examples/u_${javaJson}" overwrite="true" />
+   <copy file="${packageDir}/examples/${jsonSimple}" tofile="${packageDir}/examples/u_${jsonSimple}" overwrite="true" />
    <copy file="${packageDir}/examples/${jalviewLiteJar}" tofile="${packageDir}/examples/ap_${jalviewLiteJar}" />
-   <copy file="${packageDir}/examples/${jmolJar}" tofile="${packageDir}/examples/ap_${jmolJar}"/>
-   <copy file="${packageDir}/examples/${javaJson}" tofile="${packageDir}/examples/ap_${javaJson}"/>
-   <copy file="${packageDir}/examples/${jsonSimple}" tofile="${packageDir}/examples/ap_${jsonSimple}"/>
+   <copy file="${packageDir}/examples/${jmolJar}" tofile="${packageDir}/examples/ap_${jmolJar}" />
+   <copy file="${packageDir}/examples/${javaJson}" tofile="${packageDir}/examples/ap_${javaJson}" />
+   <copy file="${packageDir}/examples/${jsonSimple}" tofile="${packageDir}/examples/ap_${jsonSimple}" />
    <ap_applet.jar jarfile="${packageDir}/examples/ap_${jalviewLiteJar}" />
    <ap_applet.jar jarfile="${packageDir}/examples/ap_${jmolJar}" />
    <ap_applet.jar jarfile="${packageDir}/examples/ap_${javaJson}" />
    <signjar storepass="${jalview.keystore.pass}" keypass="${jalview.key.pass}" keystore="${jalview.keystore}" alias="${jalview.key}" lazy="false" verbose="false">
  
      <fileset dir="${packageDir}/examples">
-       <exclude name="u_*.jar"/>
+       <exclude name="u_*.jar" />
        <include name="${jalviewLiteJar}" />
        <include name="${jmolJar}" />
        <include name="${javaJson}" />
    </signjar>
    <!-- bizarre bug causes JmolApplet to always get signed, even if excluded from above. so copy explicitly -->
    <copy file="appletlib/${jmolJar}" tofile="${packageDir}/examples/u_${jmolJar}" overwrite="true" />
-       <!-- finally, replace any launchApp servlet tags with a version specification -->
-       <replace value="http://www.jalview.org/services/launchApp?version=${JALVIEW_VERSION}&quot;">
-               <replacetoken><![CDATA[http://www.jalview.org/services/launchApp"]]></replacetoken>
-               <fileset dir="${packageDir}/examples">
-                       <include name="**/*.html"/>
-               </fileset>
-       </replace>
-       <replace value="http://www.jalview.org/services/launchApp?version=${JALVIEW_VERSION}'">
-               <replacetoken><![CDATA[http://www.jalview.org/services/launchApp']]></replacetoken>
-               <fileset dir="${packageDir}/examples">
-                       <include name="**/*.html"/>
-               </fileset>
-       </replace>
+   <!-- finally, replace any launchApp servlet tags with a version specification -->
+   <replace value="http://www.jalview.org/services/launchApp?version=${JALVIEW_VERSION}&quot;">
+     <replacetoken>
+       <![CDATA[http://www.jalview.org/services/launchApp"]]>
+     </replacetoken>
+     <fileset dir="${packageDir}/examples">
+       <include name="**/*.html" />
+     </fileset>
+   </replace>
+   <replace value="http://www.jalview.org/services/launchApp?version=${JALVIEW_VERSION}'">
+     <replacetoken>
+       <![CDATA[http://www.jalview.org/services/launchApp']]>
+     </replacetoken>
+     <fileset dir="${packageDir}/examples">
+       <include name="**/*.html" />
+     </fileset>
+   </replace>
  
  </target>
  <target name="sourcedoc" description="Create jalview source documentation pages" depends="init">
diff --combined doc/UnitTesting.html
@@@ -1,7 -1,7 +1,7 @@@
  <html>
  <!--
 - * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
 - * Copyright (C) $$Year-Rel$$ The Jalview Authors
 + * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
 + * Copyright (C) 2015 The Jalview Authors
   * 
   * This file is part of Jalview.
   * 
@@@ -24,7 -24,7 +24,7 @@@
  <h1> Unit testing in Jalview </h1>
  
  <p>
- In June 2015, the Jalview team adopted <a href=http://testng.org/doc/index.html>TestNG</a> as the favourite unit testing framework. Consequently all existing JUnit tests were ported to TestNG.
+ In June 2015, the Jalview team adopted <a href=http://testng.org/doc/index.html>TestNG</a> for handling unit tests, and all existing JUnit tests were ported to TestNG.
  
  
  <h2>Test Groups</h2>
@@@ -89,12 -89,11 +89,11 @@@ The TestNG tests for Jalview can be exe
        A more detailed guide for installing and executing TestNG in eclipse is available at <a href=http://testng.org/doc/eclipse.html>testng.org/doc/eclipse.html</a> <br>&nbsp;
    </li>
    <li><b>From Ant:</b> 
-         </br> To execute Jalview unit test from ant please take the following steps:
+         </br>The ant task 'testng' will run Jalview's tests. You should:
        <ul>
              <li>Ensure that you have ant installed</li>
              <li>Ensure that your test classes are error free and properly annotated</li>
-             <li>Ensure that the test class or group is available in the TestNG config file. For Jalview this is located in jalview/utils/jalview_testng.xml</li>
-           <li>Add a TestNG run target to your main ant build file, and execute the target.</li>
+             <li>Specify the desired group of tests by passing a comma separated list of one or more groups via -Dtestng-groups=""</li>
          </ul> 
          A more detailed guide for executing TestNG from ant is available at <a href=http://testng.org/doc/ant.html>http://testng.org/doc/ant.html</a>
    </li>
diff --combined examples/applets.html
@@@ -1,6 -1,6 +1,6 @@@
  <!--
 - * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
 - * Copyright (C) $$Year-Rel$$ The Jalview Authors
 + * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
 + * Copyright (C) 2015 The Jalview Authors
   * 
   * This file is part of Jalview.
   * 
@@@ -168,7 -168,7 +168,7 @@@ Try out JalviewLite by pressing one of 
        secondary structure annotation</td>
      </tr>
    </table>
 -  <p>
 +<!--  <p>
      <h2>Linked Protein and cDNA alignments for a family of Steroid Receptors</h2>
    </p>
    <table width="90%">
        <td valign="center">Displays a split window view showing aligned protein
          and a reconstructed cDNA alignment.<br />Proteins were aligned with <a
          href="http://www.drive5.com/muscle">Muscle</a> (version 3.8.31,
-         via the Jalvew Desktop).<br />Data retrieved from Uniprot and
+         via the Jalview Desktop).<br />Data retrieved from Uniprot and
          ENA, after Thornton, Need and Crews, <a
          href="http://dx.doi.org/10.1126/science.1086185">Science 19
            September 2003: 301 (5640), 1714-1717</a>
        </td>
      </tr>
    </table>
 +-->
@@@ -1,6 -1,6 +1,6 @@@
  /**
 - * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
 - * Copyright (C) $$Year-Rel$$ The Jalview Authors
 + * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
 + * Copyright (C) 2015 The Jalview Authors
   * 
   * This file is part of Jalview.
   * 
@@@ -318,10 -318,8 +318,8 @@@ function _jmolhover(jmid, atomlabel, at
        }
        // use atomlabel[5] to look up model filename so we can highlight associated positions in any jalviews
        for (ap in _jvapps) {
-               _jvapps[ap].mouseOverStructure(atomlabel[2], atomlabel[3],
-                               getDocumentBase()
-                                               + "/" + 
-                                               modeltofiles[atomlabel[5]]);
+               pdb = getDocumentBase() + modeltofiles[atomlabel[5]];
+               _jvapps[ap].mouseOverStructure(atomlabel[2], atomlabel[3], pdb);
                msg = _jmolhovermsg;
        }
  }
@@@ -1,6 -1,6 +1,6 @@@
  /*
 - * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
 - * Copyright (C) $$Year-Rel$$ The Jalview Authors
 + * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
 + * Copyright (C) 2015 The Jalview Authors
   * 
   * This file is part of Jalview.
   * 
@@@ -228,7 -228,7 +228,7 @@@ public class AlignmentUtil
     * @param cdnaAlignment
     * @return
     */
-   public static boolean mapProteinToCdna(final AlignmentI proteinAlignment,
+   public static boolean mapProteinAlignmentToCdna(final AlignmentI proteinAlignment,
            final AlignmentI cdnaAlignment)
    {
      if (proteinAlignment == null || cdnaAlignment == null)
            final AlignmentI cdnaAlignment, Set<SequenceI> mappedDna,
            Set<SequenceI> mappedProtein, boolean xrefsOnly)
    {
-     boolean mappingPerformed = false;
+     boolean mappingExistsOrAdded = false;
      List<SequenceI> thisSeqs = proteinAlignment.getSequences();
      for (SequenceI aaSeq : thisSeqs)
      {
          {
            continue;
          }
-         if (!mappingExists(proteinAlignment.getCodonFrames(),
+         if (mappingExists(proteinAlignment.getCodonFrames(),
                  aaSeq.getDatasetSequence(), cdnaSeq.getDatasetSequence()))
          {
-           MapList map = mapProteinToCdna(aaSeq, cdnaSeq);
+           mappingExistsOrAdded = true;
+         }
+         else
+         {
+           MapList map = mapProteinSequenceToCdna(aaSeq, cdnaSeq);
            if (map != null)
            {
              acf.addMap(cdnaSeq, aaSeq, map);
-             mappingPerformed = true;
+             mappingExistsOrAdded = true;
              proteinMapped = true;
              mappedDna.add(cdnaSeq);
              mappedProtein.add(aaSeq);
          proteinAlignment.addCodonFrame(acf);
        }
      }
-     return mappingPerformed;
+     return mappingExistsOrAdded;
    }
  
    /**
     * @param cdnaSeq
     * @return
     */
-   public static MapList mapProteinToCdna(SequenceI proteinSeq,
+   public static MapList mapProteinSequenceToCdna(SequenceI proteinSeq,
            SequenceI cdnaSeq)
    {
      /*
       */
      final int mappedLength = 3 * aaSeqChars.length;
      int cdnaLength = cdnaSeqChars.length;
-     int cdnaStart = 1;
-     int cdnaEnd = cdnaLength;
-     final int proteinStart = 1;
-     final int proteinEnd = aaSeqChars.length;
+     int cdnaStart = cdnaSeq.getStart();
+     int cdnaEnd = cdnaSeq.getEnd();
+     final int proteinStart = proteinSeq.getStart();
+     final int proteinEnd = proteinSeq.getEnd();
  
      /*
       * If lengths don't match, try ignoring stop codon.
      /*
       * If lengths still don't match, try ignoring start codon.
       */
+     int startOffset = 0;
      if (cdnaLength != mappedLength
              && cdnaLength > 2
              && String.valueOf(cdnaSeqChars, 0, 3).toUpperCase()
                      .equals(ResidueProperties.START))
      {
+       startOffset += 3;
        cdnaStart += 3;
        cdnaLength -= 3;
      }
      {
        return null;
      }
-     if (!translatesAs(cdnaSeqChars, cdnaStart - 1, aaSeqChars))
+     if (!translatesAs(cdnaSeqChars, startOffset,
+             aaSeqChars))
      {
        return null;
      }
      /*
       * Traverse the aligned protein sequence.
       */
+     int fromOffset = alignFrom.getStart() - 1;
+     int toOffset = alignTo.getStart() - 1;
      int sourceGapMappedLength = 0;
      boolean inExon = false;
      for (char sourceChar : thatAligned)
        sourceDsPos++;
        // Note mapping positions are base 1, our sequence positions base 0
        int[] mappedPos = mapping.getMappedRegion(alignTo, alignFrom,
-               sourceDsPos);
+               sourceDsPos + fromOffset);
        if (mappedPos == null)
        {
          /*
         * But then 'align dna as protein' doesn't make much sense otherwise.
         */
        int intronLength = 0;
-       while (basesWritten < mappedCodonEnd && thisSeqPos < thisSeq.length)
+       while (basesWritten + toOffset < mappedCodonEnd
+               && thisSeqPos < thisSeq.length)
        {
          final char c = thisSeq[thisSeqPos++];
          if (c != myGapChar)
          {
            basesWritten++;
-           if (basesWritten < mappedCodonStart)
+           int sourcePosition = basesWritten + toOffset;
+           if (sourcePosition < mappedCodonStart)
            {
              /*
               * Found an unmapped (intron) base. First add in any preceding gaps
            }
            else
            {
-             final boolean startOfCodon = basesWritten == mappedCodonStart;
+             final boolean startOfCodon = sourcePosition == mappedCodonStart;
              int gapsToAdd = calculateGapsToInsert(preserveMappedGaps,
                      preserveUnmappedGaps, sourceGapMappedLength, inExon,
                      trailingCopiedGap.length(), intronLength, startOfCodon);
       * Just try to make a mapping (it is not yet stored), test whether
       * successful.
       */
-     return mapProteinToCdna(proteinDs, dnaDs) != null;
+     return mapProteinSequenceToCdna(proteinDs, dnaDs) != null;
    }
  
    /**
@@@ -1,6 -1,6 +1,6 @@@
  /*
 - * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
 - * Copyright (C) $$Year-Rel$$ The Jalview Authors
 + * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
 + * Copyright (C) 2015 The Jalview Authors
   * 
   * This file is part of Jalview.
   * 
@@@ -105,6 -105,8 +105,8 @@@ import java.util.Map
  import java.util.StringTokenizer;
  import java.util.Vector;
  
+ import javax.swing.JOptionPane;
  import org.jmol.viewer.Viewer;
  
  public class AlignFrame extends EmbmenuFrame implements ActionListener,
      alignPanel.annotationSpaceFillerHolder.addKeyListener(this);
      alignPanel.alabels.addKeyListener(this);
  
+     setAnnotationsVisibility();
      if (addToDisplay)
      {
        addToDisplay(embedded);
  
    /**
     * Set the visibility state of sequence-related and/or alignment-related
-    * annotations depending on checkbox selections. Repaint after calling.
+    * annotations depending on checkbox selections, and repaint.
     * 
     * @param visible
     */
        seqs.addElement(seq);
      }
  
-     // If the cut affects all sequences, remove highlighted columns
-     if (sg.getSize() == viewport.getAlignment().getHeight())
+     /*
+      * If the cut affects all sequences, warn, remove highlighted columns
+      */if (sg.getSize() == viewport.getAlignment().getHeight())
      {
+       boolean isEntireAlignWidth = (((sg.getEndRes() - sg.getStartRes()) + 1) == viewport
+               .getAlignment().getWidth()) ? true : false;
+       if (isEntireAlignWidth)
+       {
+         int confirm = JOptionPane.showConfirmDialog(this,
+                 MessageManager.getString("warn.delete_all"), // $NON-NLS-1$
+                 MessageManager.getString("label.delete_all"), // $NON-NLS-1$
+                 JOptionPane.OK_CANCEL_OPTION);
+         if (confirm == JOptionPane.CANCEL_OPTION
+                 || confirm == JOptionPane.CLOSED_OPTION)
+         {
+           return;
+         }
+       }
        viewport.getColumnSelection().removeElements(sg.getStartRes(),
                sg.getEndRes() + 1);
      }
              MessageManager.getString("label.sort_annotations_by_label"));
      showAutoFirst = new CheckboxMenuItem(
              MessageManager.getString("label.show_first"));
+     showAutoFirst.setState(false); // pending applet parameter
+     setShowAutoCalculatedAbove(showAutoFirst.getState());
      showAutoLast = new CheckboxMenuItem(
              MessageManager.getString("label.show_last"));
+     showAutoLast.setState(!showAutoFirst.getState());
      showAlignmentAnnotations.addItemListener(this);
      showSequenceAnnotations.addItemListener(this);
      sortAnnBySequence.addItemListener(this);
@@@ -1,6 -1,6 +1,6 @@@
  /*
 - * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
 - * Copyright (C) $$Year-Rel$$ The Jalview Authors
 + * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
 + * Copyright (C) 2015 The Jalview Authors
   * 
   * This file is part of Jalview.
   * 
@@@ -304,7 -304,7 +304,7 @@@ public class CutAndPasteTransfer extend
      }
      AlignmentI protein = thisAlignment.isNucleotide() ? al : thisAlignment;
      AlignmentI dna = thisAlignment.isNucleotide() ? thisAlignment : al;
-     boolean mapped = AlignmentUtils.mapProteinToCdna(protein, dna);
+     boolean mapped = AlignmentUtils.mapProteinAlignmentToCdna(protein, dna);
      if (!mapped)
      {
        return false;
      }
  
      /*
+      * 'align' the added alignment to match the current one
+      */
+     al.alignAs(thisAlignment);
+     /*
       * Open SplitFrame with DNA above and protein below, including the alignment
       * from textbox and a copy of the original.
       */
@@@ -1,6 -1,6 +1,6 @@@
  /*
 - * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
 - * Copyright (C) $$Year-Rel$$ The Jalview Authors
 + * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
 + * Copyright (C) 2015 The Jalview Authors
   * 
   * This file is part of Jalview.
   * 
@@@ -481,7 -481,7 +481,7 @@@ public class SeqPanel extends Panel imp
           * Convert position in sequence (base 1) to sequence character array
           * index (base 0)
           */
-         int start = m.getStart() - 1;
+         int start = m.getStart() - m.getSequence().getStart();
          setStatusMessage(seq, start, sequenceIndex);
          return true;
        }
@@@ -1,6 -1,6 +1,6 @@@
  /*
 - * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
 - * Copyright (C) $$Year-Rel$$ The Jalview Authors
 + * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
 + * Copyright (C) 2015 The Jalview Authors
   * 
   * This file is part of Jalview.
   * 
@@@ -20,7 -20,6 +20,6 @@@
   */
  package jalview.appletgui;
  
- import jalview.analysis.AlignmentUtils;
  import jalview.api.AlignmentViewPanel;
  import jalview.api.ViewStyleI;
  import jalview.bin.JalviewLite;
@@@ -44,12 -43,14 +43,14 @@@ public class SplitFrame extends Embmenu
    private Panel outermost;
  
    /**
-    * Constructor
+    * Constructs the split frame placing cdna in the top half. No 'alignment' is
+    * performed here, this should be done by the calling client if wanted.
     */
    public SplitFrame(AlignFrame af1, AlignFrame af2)
    {
-     topFrame = af1;
-     bottomFrame = af2;
+     boolean af1IsNucleotide = af1.viewport.getAlignment().isNucleotide();
+     topFrame = af1IsNucleotide ? af1 : af2;
+     bottomFrame = topFrame == af1 ? af2 : af1;
      init();
    }
  
      AlignmentViewport protein = !topAlignment.isNucleotide() ? topViewport
              : (!bottomAlignment.isNucleotide() ? bottomViewport : null);
  
-     boolean mapped = AlignmentUtils.mapProteinToCdna(
-             protein.getAlignment(), cdna.getAlignment());
-     if (mapped)
-     {
-       final StructureSelectionManager ssm = StructureSelectionManager
-               .getStructureSelectionManager(topViewport.applet);
-       ssm.registerMappings(protein.getAlignment().getCodonFrames());
-       topViewport.setCodingComplement(bottomViewport);
-       ssm.addCommandListener(cdna);
-       ssm.addCommandListener(protein);
-     }
+     final StructureSelectionManager ssm = StructureSelectionManager
+             .getStructureSelectionManager(topViewport.applet);
+     ssm.registerMappings(protein.getAlignment().getCodonFrames());
+     topViewport.setCodingComplement(bottomViewport);
+     ssm.addCommandListener(cdna);
+     ssm.addCommandListener(protein);
  
      /*
-      * Now mappings exist, can compute cDNA consensus on protein alignment
+      * Compute cDNA consensus on protein alignment
       */
      protein.initComplementConsensus();
      AlignmentViewPanel ap = topAlignment.isNucleotide() ? bottomFrame.alignPanel
@@@ -1,6 -1,6 +1,6 @@@
  /*
 - * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
 - * Copyright (C) $$Year-Rel$$ The Jalview Authors
 + * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
 + * Copyright (C) 2015 The Jalview Authors
   * 
   * This file is part of Jalview.
   * 
@@@ -417,7 -417,7 +417,7 @@@ public class Cach
              System.out.println("# INFO: Setting default net timeout to "
                      + orgtimeout + " seconds.");
            }
-           String jnlpVersion = null;
+           String remoteVersion = null;
            try
            {
              System.setProperty("sun.net.client.defaultConnectTimeout",
  
                line = line.substring(line.indexOf("value=") + 7);
                line = line.substring(0, line.lastIndexOf("\""));
-               jnlpVersion = line;
+               remoteVersion = line;
                break;
              }
            } catch (Exception ex)
            {
              System.out
-                     .println("Non-fatal exceptions when checking version at www.jalview.org :");
+                     .println("Non-fatal exception when checking version at www.jalview.org :");
              System.out.println(ex);
-             jnlpVersion = getProperty("VERSION");
+             remoteVersion = getProperty("VERSION");
            }
            System.setProperty("sun.net.client.defaultConnectTimeout",
                    orgtimeout);
  
-           setProperty("LATEST_VERSION", jnlpVersion);
+           setProperty("LATEST_VERSION", remoteVersion);
          }
        }
  
@@@ -1,6 -1,6 +1,6 @@@
  /*
 - * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
 - * Copyright (C) $$Year-Rel$$ The Jalview Authors
 + * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
 + * Copyright (C) 2015 The Jalview Authors
   * 
   * This file is part of Jalview.
   * 
@@@ -45,8 -45,10 +45,10 @@@ import jalview.io.NewickFile
  import jalview.javascript.JSFunctionExec;
  import jalview.javascript.JalviewLiteJsApi;
  import jalview.javascript.JsCallBack;
+ import jalview.javascript.MouseOverStructureListener;
  import jalview.structure.SelectionListener;
  import jalview.structure.StructureSelectionManager;
+ import jalview.util.HttpUtils;
  import jalview.util.MessageManager;
  
  import java.applet.Applet;
@@@ -61,10 -63,9 +63,9 @@@ import java.awt.event.ActionEvent
  import java.awt.event.WindowAdapter;
  import java.awt.event.WindowEvent;
  import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStream;
  import java.io.InputStreamReader;
  import java.net.URL;
+ import java.util.ArrayList;
  import java.util.Hashtable;
  import java.util.List;
  import java.util.StringTokenizer;
@@@ -92,7 -93,7 +93,7 @@@ public class JalviewLite extends Apple
    }
  
    // /////////////////////////////////////////
-   // The following public methods maybe called
+   // The following public methods may be called
    // externally, eg via javascript in HTML page
    /*
     * (non-Javadoc)
      }
    }
  
-   /*
-    * (non-Javadoc)
-    * 
+   /**
+    * Callable from javascript to register a javascript function to pass events
+    * to a structure viewer.
+    *
+    * @param listener
+    *          the name of a javascript function
+    * @param modelSet
+    *          a token separated list of PDB file names listened for
     * @see jalview.bin.JalviewLiteJsApi#setStructureListener(java.lang.String,
-    * java.lang.String)
+    *      java.lang.String)
     */
    public void setStructureListener(String listener, String modelSet)
    {
          return;
        }
      }
-     jalview.javascript.MouseOverStructureListener mol = new jalview.javascript.MouseOverStructureListener(
-             this, listener, separatorListToArray(modelSet));
+     MouseOverStructureListener mol = new MouseOverStructureListener(this,
+             listener, separatorListToArray(modelSet));
      javascriptListeners.addElement(mol);
      StructureSelectionManager.getStructureSelectionManager(this)
              .addStructureViewerListener(mol);
    /**
     * init method for Jalview Applet
     */
+   @Override
    public void init()
    {
-     // remove any handlers that might be hanging around from an earlier instance
+     debug = TRUE.equalsIgnoreCase(getParameter("debug"));
      try
      {
        if (debug)
          ex.printStackTrace();
        }
      }
-     /**
-      * turn on extra applet debugging
-      */
-     debug = TRUE.equalsIgnoreCase(getParameter("debug"));
      if (debug)
      {
        System.err.println("JalviewLite Version " + getVersion());
        System.err.println("Build Date : " + getBuildDate());
        System.err.println("Installation : " + getInstallation());
      }
      String externalsviewer = getParameter("externalstructureviewer");
      if (externalsviewer != null)
       * update the protocol state variable for accessing the datasource located
       * by file.
       * 
-      * @param file
+      * @param path
       * @return possibly updated datasource string
       */
-     public String setProtocolState(String file)
+     public String resolveFileProtocol(String path)
      {
-       if (file.startsWith("PASTE"))
+       /*
+        * is it paste data?
+        */
+       if (path.startsWith("PASTE"))
        {
-         file = file.substring(5);
          protocol = AppletFormatAdapter.PASTE;
+         return path.substring(5);
        }
-       else if (inArchive(file))
+       /*
+        * is it a URL?
+        */
+       if (path.indexOf("://") != -1)
        {
-         protocol = AppletFormatAdapter.CLASSLOADER;
+         protocol = AppletFormatAdapter.URL;
+         return path;
        }
-       else
+       /*
+        * try relative to document root
+        */
+       URL documentBase = getDocumentBase();
+       String withDocBase = resolveUrlForLocalOrAbsolute(path, documentBase);
+       if (HttpUtils.isValidUrl(withDocBase))
+       {
+         if (debug)
+         {
+           System.err.println("Prepended document base '" + documentBase
+                   + "' to make: '" + withDocBase + "'");
+         }
+         protocol = AppletFormatAdapter.URL;
+         return withDocBase;
+       }
+       /*
+        * try relative to codebase (if different to document base)
+        */
+       URL codeBase = getCodeBase();
+       String withCodeBase = applet.resolveUrlForLocalOrAbsolute(path,
+               codeBase);
+       if (!withCodeBase.equals(withDocBase)
+               && HttpUtils.isValidUrl(withCodeBase))
        {
-         file = addProtocol(file);
          protocol = AppletFormatAdapter.URL;
+         if (debug)
+         {
+           System.err.println("Prepended codebase '" + codeBase
+                   + "' to make: '" + withCodeBase + "'");
+         }
+         return withCodeBase;
+       }
+       /*
+        * try locating by classloader; try this last so files in the directory
+        * are resolved using document base
+        */
+       if (inArchive(path))
+       {
+         protocol = AppletFormatAdapter.CLASSLOADER;
        }
-       dbgMsg("Protocol identified as '" + protocol + "'");
-       return file;
+       return path;
      }
  
      public LoadingThread(String file, String file2, JalviewLite _applet)
        {
          AlignmentI al1 = af.viewport.getAlignment();
          AlignmentI al2 = af2.viewport.getAlignment();
-         if (AlignmentUtils.isMappable(al1, al2))
+         AlignmentI cdna = al1.isNucleotide() ? al1 : al2;
+         AlignmentI prot = al1.isNucleotide() ? al2 : al1;
+         if (AlignmentUtils.mapProteinAlignmentToCdna(prot, cdna))
          {
+           al2.alignAs(al1);
            SplitFrame sf = new SplitFrame(af, af2);
            sf.addToDisplay(embedded, JalviewLite.this);
            return;
        {
          return null;
        }
-       String resolvedFile = setProtocolState(fileParam);
+       String resolvedFile = resolveFileProtocol(fileParam);
        String format = new IdentifyFile().Identify(resolvedFile, protocol);
        dbgMsg("File identified as '" + format + "'");
        AlignmentI al = null;
            else
            {
              param = st.nextToken();
-             Vector tmp = new Vector();
-             Vector tmp2 = new Vector();
+             List<SequenceI> tmp = new ArrayList<SequenceI>();
+             List<String> tmp2 = new ArrayList<String>();
  
              while (st.hasMoreTokens())
              {
                if (st2.countTokens() > 1)
                {
                  // This is the chain
-                 tmp2.addElement(st2.nextToken());
+                 tmp2.add(st2.nextToken());
                  seqstring = st2.nextToken();
                }
-               tmp.addElement(matcher == null ? (Sequence) alignFrame
+               tmp.add(matcher == null ? (Sequence) alignFrame
                        .getAlignViewport().getAlignment()
                        .findName(seqstring) : matcher.findIdMatch(seqstring));
              }
  
-             seqs = new SequenceI[tmp.size()];
-             tmp.copyInto(seqs);
+             seqs = tmp.toArray(new SequenceI[tmp.size()]);
              if (tmp2.size() == tmp.size())
              {
-               chains = new String[tmp2.size()];
-               tmp2.copyInto(chains);
+               chains = tmp2.toArray(new String[tmp2.size()]);
              }
            }
-           param = setProtocolState(param);
-           if (// !jmolAvailable
-           // &&
-           protocol == AppletFormatAdapter.CLASSLOADER && !useXtrnalSviewer)
-           {
-             // Re: JAL-357 : the bug isn't a problem if we are using an
-             // external viewer!
-             // TODO: verify this Re:
-             // https://mantis.lifesci.dundee.ac.uk/view.php?id=36605
-             // This exception preserves the current behaviour where, even if
-             // the local pdb file was identified in the class loader
-             protocol = AppletFormatAdapter.URL; // this is probably NOT
-             // CORRECT!
-             param = addProtocol(param); //
-           }
+           param = resolveFileProtocol(param);
+           // TODO check JAL-357 for files in a jar (CLASSLOADER)
            pdb.setFile(param);
  
            if (seqs != null)
        {
          try
          {
-           param = setProtocolState(param);
+           param = resolveFileProtocol(param);
            JPredFile predictions = new JPredFile(param, protocol);
            JnetAnnotationMaker.add_annotation(predictions,
                    alignFrame.viewport.getAlignment(), 0, false);
        String param = applet.getParameter("annotations");
        if (param != null)
        {
-         param = setProtocolState(param);
+         param = resolveFileProtocol(param);
  
          if (new AnnotationFile().annotateAlignmentView(alignFrame.viewport,
                  param, protocol))
        param = applet.getParameter("features");
        if (param != null)
        {
-         param = setProtocolState(param);
+         param = resolveFileProtocol(param);
  
          result = alignFrame.parseFeaturesFile(param, protocol);
        }
        {
          try
          {
-           treeFile = setProtocolState(treeFile);
+           treeFile = resolveFileProtocol(treeFile);
            NewickFile fin = new NewickFile(treeFile, protocol);
            fin.parse();
  
      /**
       * Discovers whether the given file is in the Applet Archive
       * 
-      * @param file
+      * @param f
       *          String
       * @return boolean
       */
-     boolean inArchive(String file)
+     boolean inArchive(String f)
      {
        // This might throw a security exception in certain browsers
        // Netscape Communicator for instance.
        try
        {
-         boolean rtn = (getClass().getResourceAsStream("/" + file) != null);
+         boolean rtn = (getClass().getResourceAsStream("/" + f) != null);
          if (debug)
          {
-           System.err.println("Resource '" + file + "' was "
-                   + (rtn ? "" : "not") + " located by classloader.");
+           System.err.println("Resource '" + f + "' was "
+                   + (rtn ? "" : "not ") + "located by classloader.");
          }
          return rtn;
        } catch (Exception ex)
        {
-         System.out.println("Exception checking resources: " + file + " "
+         System.out.println("Exception checking resources: " + f + " "
                  + ex);
          return false;
        }
      }
-     /**
-      * If the file is not already in URL format, tries to locate it by resolving
-      * as a URL.
-      * 
-      * @param f
-      * @return
-      */
-     String addProtocol(final String f)
-     {
-       if (f.indexOf("://") != -1)
-       {
-         // already has URL format
-         return f;
-       }
-       /*
-        * Try relative to document base
-        */
-       URL documentBase = getDocumentBase();
-       String url = applet.resolveUrlForLocalOrAbsolute(f, documentBase);
-       if (urlExists(url))
-       {
-         if (debug)
-         {
-           System.err.println("Prepended document base '" + documentBase
-                   + "' to make: '" + url + "'");
-         }
-         return url;
-       }
-       /*
-        * Try relative to codebase
-        */
-       URL codeBase = getCodeBase();
-       url = applet.resolveUrlForLocalOrAbsolute(f, codeBase);
-       if (urlExists(url))
-       {
-         if (debug)
-         {
-           System.err.println("Prepended codebase '" + codeBase
-                   + "' to make: '" + url + "'");
-         }
-         return url;
-       }
-       return f;
-     }
-     /**
-      * Returns true if an input stream can be opened on the specified URL, else
-      * false.
-      * 
-      * @param url
-      * @return
-      */
-     private boolean urlExists(String url)
-     {
-       InputStream is = null;
-       try
-       {
-         is = new URL(url).openStream();
-         if (is != null)
-         {
-           return true;
-         }
-       } catch (Exception x)
-       {
-         // ignore
-       } finally
-       {
-         if (is != null)
-         {
-           try
-           {
-             is.close();
-           } catch (IOException e)
-           {
-             // ignore
-           }
-         }
-       }
-       return false;
-     }
    }
  
    /**
     * @param separator
     * @return elements separated by separator
     */
-   public String[] separatorListToArray(String list, String separator)
+   public static String[] separatorListToArray(String list, String separator)
    {
+     // TODO use StringUtils version (slightly different...)
      int seplen = separator.length();
      if (list == null || list.equals("") || list.equals(separator))
      {
     * @param separator
     * @return concatenated string
     */
-   public String arrayToSeparatorList(String[] list, String separator)
+   public static String arrayToSeparatorList(String[] list, String separator)
    {
+     // TODO use StringUtils version
      StringBuffer v = new StringBuffer();
      if (list != null && list.length > 0)
      {
     * form a complete URL given a path to a resource and a reference location on
     * the same server
     * 
-    * @param url
+    * @param targetPath
     *          - an absolute path on the same server as localref or a document
     *          located relative to localref
     * @param localref
     *          - a URL on the same server as url
     * @return a complete URL for the resource located by url
     */
-   private String resolveUrlForLocalOrAbsolute(String url, URL localref)
+   private String resolveUrlForLocalOrAbsolute(String targetPath,
+           URL localref)
    {
-     String codebase = localref.toString();
-     if (url.indexOf("/") == 0)
+     String resolvedPath = "";
+     /*
+      * get URL path and strip off any trailing file e.g.
+      * www.jalview.org/examples/index.html#applets?a=b is trimmed to
+      * www.jalview.org/examples/
+      */
+     String urlPath = localref.toString();
+     String directoryPath = urlPath;
+     int lastSeparator = directoryPath.lastIndexOf("/");
+     if (lastSeparator > 0)
+     {
+       directoryPath = directoryPath.substring(0, lastSeparator + 1);
+     }
+     if (targetPath.startsWith("/"))
      {
-       String localfile = localref.getFile();
-       url = codebase.substring(0, codebase.length() - localfile.length())
-               + url;
+       /*
+        * construct absolute URL to a file on the server - this is not allowed?
+        */
+       // String localfile = localref.getFile();
+       // resolvedPath = urlPath.substring(0,
+       // urlPath.length() - localfile.length())
+       // + targetPath;
+       resolvedPath = directoryPath + targetPath.substring(1);
      }
      else
      {
-       url = localref + url;
+       resolvedPath = directoryPath + targetPath;
      }
      if (debug)
      {
-       System.err.println("URL: " + localref.toString());
-       System.err.println("URL.getFile: " + localref.getFile());
-       System.err.println("URL.getPath: " + localref.getPath());
-       System.err.println("URL.getQuery: " + localref.getQuery());
-       System.err.println("returning " + url);
+       System.err.println("resolveUrlForLocalOrAbsolute returning "
+               + resolvedPath);
      }
-     return url;
+     return resolvedPath;
    }
  
    /**
          URL prepend;
          url = resolveUrlForLocalOrAbsolute(
                  url,
-                 prepend = getDefaultParameter("resolvetocodebase", false) ? getDocumentBase()
-                         : getCodeBase());
+                 prepend = getDefaultParameter("resolvetocodebase", false) ? getCodeBase()
+                         : getDocumentBase());
          if (debug)
          {
            System.err
@@@ -1,6 -1,6 +1,6 @@@
  /*
 - * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
 - * Copyright (C) $$Year-Rel$$ The Jalview Authors
 + * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
 + * Copyright (C) 2015 The Jalview Authors
   * 
   * This file is part of Jalview.
   * 
@@@ -1220,10 -1220,15 +1220,15 @@@ public class EditCommand implements Com
          for (SequenceI seq : e.getSequences())
          {
            SequenceI ds = seq.getDatasetSequence();
-           SequenceI preEdit = result.get(ds);
-           if (preEdit == null)
+           // SequenceI preEdit = result.get(ds);
+           if (!result.containsKey(ds))
            {
-             preEdit = new Sequence("", seq.getSequenceAsString());
+             /*
+              * copy sequence including start/end (but don't use copy constructor
+              * as we don't need annotations)
+              */
+             SequenceI preEdit = new Sequence("", seq.getSequenceAsString(),
+                     seq.getStart(), seq.getEnd());
              preEdit.setDatasetSequence(ds);
              result.put(ds, preEdit);
            }
       * Work backwards through the edit list, deriving the sequences before each
       * was applied. The final result is the sequence set before any edits.
       */
-     Iterator<Edit> edits = new ReverseListIterator<Edit>(getEdits());
-     while (edits.hasNext())
+     Iterator<Edit> editList = new ReverseListIterator<Edit>(getEdits());
+     while (editList.hasNext())
      {
-       Edit oldEdit = edits.next();
+       Edit oldEdit = editList.next();
        Action action = oldEdit.getAction();
        int position = oldEdit.getPosition();
        int number = oldEdit.getNumber();
          SequenceI preEdit = result.get(ds);
          if (preEdit == null)
          {
-           preEdit = new Sequence("", seq.getSequenceAsString());
+           preEdit = new Sequence("", seq.getSequenceAsString(),
+                   seq.getStart(), seq.getEnd());
            preEdit.setDatasetSequence(ds);
            result.put(ds, preEdit);
          }
@@@ -1,6 -1,6 +1,6 @@@
  /*
 - * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
 - * Copyright (C) $$Year-Rel$$ The Jalview Authors
 + * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
 + * Copyright (C) 2015 The Jalview Authors
   * 
   * This file is part of Jalview.
   * 
@@@ -399,13 -399,13 +399,13 @@@ public class AlignedCodonFram
        return null;
      }
      MapList ml = null;
-     char[] dnaSeq = null;
+     SequenceI dnaSeq = null;
      for (int i = 0; i < dnaToProt.length; i++)
      {
        if (dnaToProt[i].to == protein)
        {
          ml = getdnaToProt()[i];
-         dnaSeq = dnaSeqs[i].getSequence();
+         dnaSeq = dnaSeqs[i];
          break;
        }
      }
       * Read off the mapped nucleotides (converting to position base 0)
       */
      codonPos = MappingUtils.flattenRanges(codonPos);
-     return new char[] { dnaSeq[codonPos[0] - 1], dnaSeq[codonPos[1] - 1],
-         dnaSeq[codonPos[2] - 1] };
+     char[] dna = dnaSeq.getSequence();
+     int start = dnaSeq.getStart();
+     return new char[] { dna[codonPos[0] - start], dna[codonPos[1] - start],
+         dna[codonPos[2] - start] };
    }
  
    /**
@@@ -1,6 -1,6 +1,6 @@@
  /*
 - * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
 - * Copyright (C) $$Year-Rel$$ The Jalview Authors
 + * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
 + * Copyright (C) 2015 The Jalview Authors
   * 
   * This file is part of Jalview.
   * 
@@@ -48,6 -48,11 +48,11 @@@ public class Mappin
      private final char[] alignedSeq;
  
      /*
+      * the sequence start residue
+      */
+     private int start;
+     /*
       * Next position (base 0) in the aligned sequence
       */
      private int alignedColumn = 0;
      /**
       * Constructor
       * 
-      * @param cs
-      *          the aligned sequence characters
+      * @param seq
+      *          the aligned sequence
       * @param gapChar
       */
-     public AlignedCodonIterator(char[] cs, char gapChar)
+     public AlignedCodonIterator(SequenceI seq, char gapChar)
      {
-       this.alignedSeq = cs;
+       this.alignedSeq = seq.getSequence();
+       this.start = seq.getStart();
        this.gap = gapChar;
        fromRanges = map.getFromRanges().iterator();
        toRanges = map.getToRanges().iterator();
        // i.e. code like getNextCodon()
        if (toPosition <= currentToRange[1])
        {
-         char pep = Mapping.this.to.getSequence()[toPosition - 1];
+         SequenceI seq = Mapping.this.to;
+         char pep = seq.getSequence()[toPosition - seq.getStart()];
          toPosition++;
          return String.valueOf(pep);
        }
       */
      private int getAlignedColumn(int sequencePos)
      {
-       while (alignedBases < sequencePos
+       /*
+        * allow for offset e.g. treat pos 8 as 2 if sequence starts at 7
+        */
+       int truePos = sequencePos - (start - 1);
+       while (alignedBases < truePos
                && alignedColumn < alignedSeq.length)
        {
          if (alignedSeq[alignedColumn++] != gap)
  
    public Iterator<AlignedCodon> getCodonIterator(SequenceI seq, char gapChar)
    {
-     return new AlignedCodonIterator(seq.getSequence(), gapChar);
+     return new AlignedCodonIterator(seq, gapChar);
    }
  
  }
@@@ -1,6 -1,6 +1,6 @@@
  /*
 - * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
 - * Copyright (C) $$Year-Rel$$ The Jalview Authors
 + * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
 + * Copyright (C) 2015 The Jalview Authors
   * 
   * This file is part of Jalview.
   * 
@@@ -368,6 -368,9 +368,9 @@@ public class AlignFrame extends GAlignF
        setGUINucleotide(viewport.getAlignment().isNucleotide());
      }
  
+     this.alignPanel.av
+             .setShowAutocalculatedAbove(isShowAutoCalculatedAbove());
      setMenusFromViewport(viewport);
      buildSortByAnnotationScoresMenu();
      buildTreeMenu();
      int startPos = aligmentStartEnd[0];
      int endPos = aligmentStartEnd[1];
  
-     int[] lowestRange = new int[2];
-     int[] higestRange = new int[2];
+     int[] lowestRange = new int[] { -1, -1 };
+     int[] higestRange = new int[] { -1, -1 };
  
      for (int[] hiddenCol : hiddenCols)
      {
        lowestRange = (hiddenCol[0] <= startPos) ? hiddenCol : lowestRange;
        higestRange = (hiddenCol[1] >= endPos) ? hiddenCol : higestRange;
      }
-     // System.out.println("min : " + lowestRange[0] + "-" + lowestRange[1]);
-     // System.out.println("max : " + higestRange[0] + "-" + higestRange[1]);
  
-     if (lowestRange[0] == 0 && lowestRange[1] == 0)
+     if (lowestRange[0] == -1 && lowestRange[1] == -1)
      {
        startPos = aligmentStartEnd[0];
      }
        startPos = lowestRange[1] + 1;
      }
  
-     if (higestRange[0] == 0 && higestRange[1] == 0)
+     if (higestRange[0] == -1 && higestRange[1] == -1)
      {
        endPos = aligmentStartEnd[1];
      }
      else
      {
-       endPos = higestRange[0];
+       endPos = higestRange[0] - 1;
      }
  
-     // System.out.println("Export range : " + minPos + " - " + maxPos);
+     // System.out.println("Export range : " + startPos + " - " + endPos);
      return new int[] { startPos, endPos };
    }
  
    public static void main(String[] args)
    {
      ArrayList<int[]> hiddenCols = new ArrayList<int[]>();
-     hiddenCols.add(new int[] { 0, 4 });
+     hiddenCols.add(new int[] { 0, 0 });
      hiddenCols.add(new int[] { 6, 9 });
      hiddenCols.add(new int[] { 11, 12 });
      hiddenCols.add(new int[] { 33, 33 });
-     hiddenCols.add(new int[] { 45, 50 });
+     hiddenCols.add(new int[] { 50, 50 });
  
      int[] x = getStartEnd(new int[] { 0, 50 }, hiddenCols);
      // System.out.println("Export range : " + x[0] + " - " + x[1]);
       */
      if (sg.getSize() == viewport.getAlignment().getHeight())
      {
-       int confirm = JOptionPane.showConfirmDialog(this,
-               MessageManager.getString("warn.delete_all"), // $NON-NLS-1$
-               MessageManager.getString("label.delete_all"), // $NON-NLS-1$
-               JOptionPane.OK_CANCEL_OPTION);
-       if (confirm == JOptionPane.CANCEL_OPTION
-               || confirm == JOptionPane.CLOSED_OPTION)
+       boolean isEntireAlignWidth = (((sg.getEndRes() - sg.getStartRes()) + 1) == viewport
+               .getAlignment().getWidth()) ? true : false;
+       if (isEntireAlignWidth)
        {
-         return;
+         int confirm = JOptionPane.showConfirmDialog(this,
+                 MessageManager.getString("warn.delete_all"), // $NON-NLS-1$
+                 MessageManager.getString("label.delete_all"), // $NON-NLS-1$
+                 JOptionPane.OK_CANCEL_OPTION);
+         if (confirm == JOptionPane.CANCEL_OPTION
+                 || confirm == JOptionPane.CLOSED_OPTION)
+         {
+           return;
+         }
        }
        viewport.getColumnSelection().removeElements(sg.getStartRes(),
                sg.getEndRes() + 1);
      }
      SequenceI[] cut = sg.getSequences()
              .toArray(new SequenceI[sg.getSize()]);
  
      }
    }
  
    /**
     * DOCUMENT ME!
     * 
@@@ -1,6 -1,6 +1,6 @@@
  /*
 - * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
 - * Copyright (C) $$Year-Rel$$ The Jalview Authors
 + * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
 + * Copyright (C) 2015 The Jalview Authors
   * 
   * This file is part of Jalview.
   * 
@@@ -926,7 -926,7 +926,7 @@@ public class AlignViewport extends Alig
       * is a pre-requisite for building mappings.
       */
      al.setDataset(null);
-     AlignmentUtils.mapProteinToCdna(protein, cdna);
+     AlignmentUtils.mapProteinAlignmentToCdna(protein, cdna);
  
      /*
       * Create the AlignFrame for the added alignment. If it is protein, mappings
@@@ -1,6 -1,6 +1,6 @@@
  /*
 - * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
 - * Copyright (C) $$Year-Rel$$ The Jalview Authors
 + * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
 + * Copyright (C) 2015 The Jalview Authors
   * 
   * This file is part of Jalview.
   * 
@@@ -3250,8 -3250,8 +3250,8 @@@ public class Jalview2XM
       * indicate that annotation colours are applied across all groups (pre
       * Jalview 2.8.1 behaviour)
       */
-     boolean doGroupAnnColour = isVersionStringLaterThan("2.8.1",
-             object.getVersion());
+     boolean doGroupAnnColour = Jalview2XML.isVersionStringLaterThan(
+             "2.8.1", object.getVersion());
  
      AlignmentPanel ap = null;
      boolean isnewview = true;
     * @return true if version is development/null or evaluates to the same or
     *         later X.Y.Z (where X,Y,Z are like [0-9]+b?[0-9]*)
     */
-   protected boolean isVersionStringLaterThan(String supported,
+   public static boolean isVersionStringLaterThan(String supported,
            String version)
    {
      if (version == null || version.equalsIgnoreCase("DEVELOPMENT BUILD")
          String fileT = fileV.nextToken().toLowerCase().replace('b', '.');
          try
          {
-           if (Float.valueOf(curT) > Float.valueOf(fileT))
+           float supportedVersionToken = Float.parseFloat(curT);
+           float myVersiontoken = Float.parseFloat(fileT);
+           if (supportedVersionToken > myVersiontoken)
            {
              // current version is newer than the version that wrote the file
              return false;
            }
+           if (supportedVersionToken < myVersiontoken)
+           {
+             // current version is older than the version that wrote the file
+             return true;
+           }
          } catch (NumberFormatException nfe)
          {
            System.err
@@@ -1,6 -1,6 +1,6 @@@
  /*
 - * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
 - * Copyright (C) $$Year-Rel$$ The Jalview Authors
 + * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
 + * Copyright (C) 2015 The Jalview Authors
   * 
   * This file is part of Jalview.
   * 
@@@ -904,7 -904,7 +904,7 @@@ public class SeqPanel extends JPanel im
           * Convert position in sequence (base 1) to sequence character array
           * index (base 0)
           */
-         int start = m.getStart() - 1;
+         int start = m.getStart() - m.getSequence().getStart();
          setStatusMessage(seq, start, sequenceIndex);
          return;
        }
@@@ -1,6 -1,6 +1,6 @@@
  /*
 - * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
 - * Copyright (C) $$Year-Rel$$ The Jalview Authors
 + * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
 + * Copyright (C) 2015 The Jalview Authors
   * 
   * This file is part of Jalview.
   * 
@@@ -115,11 -115,7 +115,7 @@@ public class BioJsHTMLOutpu
        AlignmentExportData exportData = jalview.gui.AlignFrame
                .getAlignmentForExport(JSONFile.FILE_DESC,
                        ap.getAlignViewport(), exportSettings);
-       if (exportData.getSettings().isCancelled())
-       {
-         return;
-       }
-       String jalviewAlignmentJson = new FormatAdapter(ap,
+       String bioJSON = new FormatAdapter(ap,
                exportData.getSettings()).formatSequences(JSONFile.FILE_DESC,
                exportData.getAlignment(), exportData.getOmitHidden(),
                exportData.getStartEndPostions(), ap.getAlignViewport()
  
        String bioJSTemplateString = getBioJsTemplateAsString();
        String generatedBioJsWithJalviewAlignmentAsJson = bioJSTemplateString
-               .replaceAll("#sequenceData#", jalviewAlignmentJson)
+               .replaceAll("#sequenceData#", bioJSON)
                .toString();
  
        PrintWriter out = new java.io.PrintWriter(new java.io.FileWriter(
@@@ -1,6 -1,6 +1,6 @@@
  /*
 - * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
 - * Copyright (C) $$Year-Rel$$ The Jalview Authors
 + * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
 + * Copyright (C) 2015 The Jalview Authors
   * 
   * This file is part of Jalview.
   * 
@@@ -107,7 -107,6 +107,6 @@@ public class FormatAdapter extends Appl
        {
          startRes = seqs[i].getStart();
          endRes = seqs[i].getEnd();
          if (startEnd != null)
          {
            startIndex = startEnd[0];
            }
  
            startRes = seqs[i].findPosition(startIndex);
-           startRes = seqs[i].getStart() > 1 ? startRes - seqs[i].getStart()
-                   : startRes;
-           endRes = seqs[i].findPosition(endIndex) - seqs[i].getStart();
+           endRes = seqs[i].findPosition(endIndex);
          }
  
          tmp[i] = new Sequence(seqs[i].getName(), omitHiddenColumns[i],
@@@ -1,6 -1,6 +1,6 @@@
  /*
 - * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
 - * Copyright (C) $$Year-Rel$$ The Jalview Authors
 + * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
 + * Copyright (C) 2015 The Jalview Authors
   * 
   * This file is part of Jalview.
   * 
@@@ -503,8 -503,9 +503,9 @@@ public class JSONFile extends AlignFil
                      .valueOf(annot.get("value").toString());
              String desc = annot.get("description") == null ? null : annot
                      .get("description").toString();
-             char ss = annot.get("secondaryStructure") == null ? ' ' : annot
+             char ss = annot.get("secondaryStructure") == null
+                     || annot.get("secondaryStructure").toString()
+                             .equalsIgnoreCase("u0000") ? ' ' : annot
                      .get("secondaryStructure").toString().charAt(0);
              String displayChar = annot.get("displayCharacter") == null ? ""
                      : annot.get("displayCharacter").toString();
      for (JalviewBioJsColorSchemeMapper cs : JalviewBioJsColorSchemeMapper
              .values())
      {
-       if (cs.getBioJsName().equalsIgnoreCase(bioJsColourSchemeName))
+       if (cs.getBioJsName().equalsIgnoreCase(bioJsColourSchemeName)
+               || cs.getJalviewName()
+                       .equalsIgnoreCase(bioJsColourSchemeName))
        {
          jalviewColor = cs.getJvColourScheme();
          break;
@@@ -1,6 -1,6 +1,6 @@@
  /*
 - * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
 - * Copyright (C) $$Year-Rel$$ The Jalview Authors
 + * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
 + * Copyright (C) 2015 The Jalview Authors
   * 
   * This file is part of Jalview.
   * 
@@@ -32,7 -32,11 +32,11 @@@ import jalview.structure.StructureListe
  import jalview.structure.StructureMapping;
  import jalview.structure.StructureMappingcommandSet;
  import jalview.structure.StructureSelectionManager;
+ import jalview.util.HttpUtils;
  
+ import java.io.IOException;
+ import java.io.InputStream;
+ import java.net.URL;
  import java.util.ArrayList;
  import java.util.List;
  
@@@ -87,47 -91,43 +91,43 @@@ public class MouseOverStructureListene
      {
        for (int i = 0; i < modelSet.length; i++)
        {
-         // resolve a real filename
-         try
-         {
-           if (new java.net.URL(modelSet[i]).openConnection() != null)
-           {
-             continue;
-           }
-         } catch (Exception x)
-         {
-         }
-         ;
-         try
-         {
-           String db = jvlite.getDocumentBase().toString();
-           db = db.substring(0, db.lastIndexOf("/"));
-           if (new java.net.URL(db + "/" + modelSet[i]).openConnection() != null)
-           {
-             modelSet[i] = db + "/" + modelSet[i];
-             continue;
-           }
-         } catch (Exception x)
-         {
-         }
-         ;
-         try
-         {
-           if (new java.net.URL(jvlite.getCodeBase() + modelSet[i])
-                   .openConnection() != null)
-           {
-             modelSet[i] = jvlite.getCodeBase() + modelSet[i];
-             continue;
-           }
-         } catch (Exception x)
-         {
-         }
-         ;
+         modelSet[i] = resolveModelFile(modelSet[i]);
        }
      }
    }
  
+   /**
+    * Returns the first out of: file, file prefixed by document base, or file
+    * prefixed by codebase which can be resolved to a valid URL. If none can,
+    * returns the input parameter value.
+    * 
+    * @param file
+    */
+   public String resolveModelFile(String file)
+   {
+     // TODO reuse JalviewLite.LoadingThread.addProtocol instead
+     if (HttpUtils.isValidUrl(file))
+     {
+       return file;
+     }
+     String db = jvlite.getDocumentBase().toString();
+     db = db.substring(0, db.lastIndexOf("/"));
+     String docBaseFile = db + "/" + file;
+     if (HttpUtils.isValidUrl(docBaseFile))
+     {
+       return docBaseFile;
+     }
+     String cb = jvlite.getCodeBase() + file;
+     if (HttpUtils.isValidUrl(cb))
+     {
+       return cb;
+     }
+     return file;
+   }
    @Override
    public String[] getPdbFile()
    {
@@@ -1,6 -1,6 +1,6 @@@
  /*
 - * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
 - * Copyright (C) $$Year-Rel$$ The Jalview Authors
 + * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
 + * Copyright (C) 2015 The Jalview Authors
   * 
   * This file is part of Jalview.
   * 
@@@ -1295,8 -1295,10 +1295,10 @@@ public class GAlignFrame extends JInter
              MessageManager.getString("label.show_last"));
      buttonGroup.add(showAutoFirst);
      buttonGroup.add(showAutoLast);
-     showAutoFirst.setSelected(Cache.getDefault(
-             Preferences.SHOW_AUTOCALC_ABOVE, false));
+     final boolean autoFirst = Cache.getDefault(
+             Preferences.SHOW_AUTOCALC_ABOVE, false);
+     showAutoFirst.setSelected(autoFirst);
+     setShowAutoCalculatedAbove(autoFirst);
      showAutoFirst.addActionListener(new ActionListener()
      {
        @Override
@@@ -1,6 -1,6 +1,6 @@@
  /*
 - * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
 - * Copyright (C) $$Year-Rel$$ The Jalview Authors
 + * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
 + * Copyright (C) 2015 The Jalview Authors
   * 
   * This file is part of Jalview.
   * 
@@@ -28,7 -28,7 +28,7 @@@ public class SequencePoj
      required = true,
      minLength = 3,
      maxLength = 2147483647,
-     description = "Sequence residue characters. An aligned sequence may contain <br>one of the following gap characters Ã¢\80\9c.â\80?, Ã¢\80\9c-â\80? or Ã¢\80\9c Ã¢\80?")
+     description = "Sequence residue characters. An aligned sequence may contain <br>one of the following gap characters &#x201c;.&#x201d;, &#x201c;-&#x201d; or &#x201c;&nbsp;&#x201d;")
    private String seq;
  
    @Attributes(required = true, description = "Sequence name")
@@@ -1,6 -1,6 +1,6 @@@
  /*
 - * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
 - * Copyright (C) $$Year-Rel$$ The Jalview Authors
 + * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
 + * Copyright (C) 2015 The Jalview Authors
   * 
   * This file is part of Jalview.
   * 
@@@ -125,9 -125,13 +125,13 @@@ public class DBRefFetcher implements Ru
      {
        alseqs[i] = seqs[i];
        if (seqs[i].getDatasetSequence() != null)
+       {
          ds[i] = seqs[i].getDatasetSequence();
+       }
        else
+       {
          ds[i] = seqs[i];
+       }
      }
      this.dataset = ds;
      // TODO Jalview 2.5 lots of this code should be in the gui package!
                  sequence.getSequenceAsString()).toUpperCase();
  
          int absStart = entrySeq.indexOf(nonGapped);
-         int mapStart = entry.getStart();
-         jalview.datamodel.Mapping mp;
+         Mapping mp;
  
+         final int sequenceStart = sequence.getStart();
          if (absStart == -1)
          {
            // Is local sequence contained in dataset sequence?
            // create valid mapping between matching region of local sequence and
            // the mapped sequence
            mp = new Mapping(null, new int[] {
-               sequence.getStart() + absStart,
-               sequence.getStart() + absStart + entrySeq.length() - 1 },
+               sequenceStart + absStart,
+               sequenceStart + absStart + entrySeq.length() - 1 },
                    new int[] { entry.getStart(),
                        entry.getStart() + entrySeq.length() - 1 }, 1, 1);
            updateRefFrame = false; // mapping is based on current start/end so
              if (sequence.getSequenceFeatures() != null)
              {
                SequenceFeature[] sf = sequence.getSequenceFeatures();
-               int start = sequence.getStart();
+               int start = sequenceStart;
                int end = sequence.getEnd();
                int startShift = 1 - absStart - start; // how much the features
                                                       // are
                  + " from " + dbSource + " sequence : " + entry.getName());
          sequence.transferAnnotation(entry, mp);
          // unknownSequences.remove(sequence);
-         int absEnd = absStart + nonGapped.length();
-         absStart += 1;
+         absStart += entry.getStart();
+         int absEnd = absStart + nonGapped.length() - 1;
          if (!trimDatasetSeqs)
          {
            // insert full length sequence from record
@@@ -1,6 -1,6 +1,6 @@@
  /*
 - * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
 - * Copyright (C) $$Year-Rel$$ The Jalview Authors
 + * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
 + * Copyright (C) 2015 The Jalview Authors
   * 
   * This file is part of Jalview.
   * 
@@@ -281,7 -281,7 +281,7 @@@ public class AlignmentUtilsTest
     * @throws IOException
     */
    @Test(groups = { "Functional" })
-   public void testMapProteinToCdna_noXrefs() throws IOException
+   public void testMapProteinAlignmentToCdna_noXrefs() throws IOException
    {
      List<SequenceI> protseqs = new ArrayList<SequenceI>();
      protseqs.add(new Sequence("UNIPROT|V12345", "EIQ"));
      AlignmentI cdna = new Alignment(dnaseqs.toArray(new SequenceI[4]));
      cdna.setDataset(null);
  
-     assertTrue(AlignmentUtils.mapProteinToCdna(protein, cdna));
+     assertTrue(AlignmentUtils.mapProteinAlignmentToCdna(protein, cdna));
  
      // 3 mappings made, each from 1 to 1 sequence
      assertEquals(3, protein.getCodonFrames().size());
     * @throws IOException
     */
    @Test(groups = { "Functional" })
-   public void testMapProteinToCdna_withStartAndStopCodons()
+   public void testMapProteinAlignmentToCdna_withStartAndStopCodons()
            throws IOException
    {
      List<SequenceI> protseqs = new ArrayList<SequenceI>();
      AlignmentI cdna = new Alignment(dnaseqs.toArray(new SequenceI[4]));
      cdna.setDataset(null);
  
-     assertTrue(AlignmentUtils.mapProteinToCdna(protein, cdna));
+     assertTrue(AlignmentUtils.mapProteinAlignmentToCdna(protein, cdna));
  
      // 3 mappings made, each from 1 to 1 sequence
      assertEquals(3, protein.getCodonFrames().size());
     * @throws IOException
     */
    @Test(groups = { "Functional" })
-   public void testMapProteinToCdna_withXrefs() throws IOException
+   public void testMapProteinAlignmentToCdna_withXrefs() throws IOException
    {
      List<SequenceI> protseqs = new ArrayList<SequenceI>();
      protseqs.add(new Sequence("UNIPROT|V12345", "EIQ"));
      // A11111 should be mapped to V12347
      // A55555 is spare and has no xref so is not mapped
  
-     assertTrue(AlignmentUtils.mapProteinToCdna(protein, cdna));
+     assertTrue(AlignmentUtils.mapProteinAlignmentToCdna(protein, cdna));
  
      // 4 protein mappings made for 3 proteins, 2 to V12345, 1 each to V12346/7
      assertEquals(3, protein.getCodonFrames().size());
     * @throws IOException
     */
    @Test(groups = { "Functional" })
-   public void testMapProteinToCdna_prioritiseXrefs() throws IOException
+   public void testMapProteinAlignmentToCdna_prioritiseXrefs()
+           throws IOException
    {
      List<SequenceI> protseqs = new ArrayList<SequenceI>();
      protseqs.add(new Sequence("UNIPROT|V12345", "EIQ"));
      // A11111 should then be mapped to the unmapped V12346
      dnaseqs.get(1).addDBRef(new DBRefEntry("UNIPROT", "1", "V12345"));
  
-     assertTrue(AlignmentUtils.mapProteinToCdna(protein, cdna));
+     assertTrue(AlignmentUtils.mapProteinAlignmentToCdna(protein, cdna));
  
      // 2 protein mappings made
      assertEquals(2, protein.getCodonFrames().size());
      assertTrue(AlignmentUtils.isMappable(al1, al2));
      assertTrue(AlignmentUtils.isMappable(al2, al1));
    }
+   /**
+    * Test creating a mapping when the sequences involved do not start at residue
+    * 1
+    * 
+    * @throws IOException
+    */
+   @Test(groups = { "Functional" })
+   public void testMapProteinSequenceToCdna_forSubsequence()
+           throws IOException
+   {
+     SequenceI prot = new Sequence("UNIPROT|V12345", "E-I--Q", 10, 12);
+     prot.createDatasetSequence();
+     SequenceI dna = new Sequence("EMBL|A33333", "GAA--AT-C-CAG", 40, 48);
+     dna.createDatasetSequence();
+     MapList map = AlignmentUtils.mapProteinSequenceToCdna(prot, dna);
+     assertEquals(10, map.getToLowest());
+     assertEquals(12, map.getToHighest());
+     assertEquals(40, map.getFromLowest());
+     assertEquals(48, map.getFromHighest());
+   }
  }
@@@ -1,6 -1,6 +1,6 @@@
  /*
 - * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
 - * Copyright (C) $$Year-Rel$$ The Jalview Authors
 + * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
 + * Copyright (C) 2015 The Jalview Authors
   * 
   * This file is part of Jalview.
   * 
@@@ -105,7 -105,7 +105,7 @@@ public class EditCommandTes
    public void testCut()
    {
      Edit ec = testee.new Edit(Action.CUT, seqs, 4, 3, al);
-     testee.cut(ec, new AlignmentI[] { al });
+     EditCommand.cut(ec, new AlignmentI[] { al });
      assertEquals("abcdhjk", seqs[0].getSequenceAsString());
      assertEquals("fghjnopq", seqs[1].getSequenceAsString());
      assertEquals("qrstxyz", seqs[2].getSequenceAsString());
      newSeqs[1] = new Sequence("newseq1", "JWMPDH");
  
      Edit ec = testee.new Edit(Action.PASTE, newSeqs, 0, al.getWidth(), al);
-     testee.paste(ec, new AlignmentI[] { al });
+     EditCommand.paste(ec, new AlignmentI[] { al });
      assertEquals(6, al.getSequences().size());
      assertEquals("1234567890", seqs[3].getSequenceAsString());
      assertEquals("ACEFKL", seqs[4].getSequenceAsString());
      SequenceI seq = new Sequence("", "--A--B-CDEF");
      SequenceI ds = new Sequence("", "ABCDEF");
      seq.setDatasetSequence(ds);
-     SequenceI[] seqs = new SequenceI[] { seq };
-     Edit e = command.new Edit(Action.INSERT_GAP, seqs, 1, 2, '-');
+     SequenceI[] sqs = new SequenceI[] { seq };
+     Edit e = command.new Edit(Action.INSERT_GAP, sqs, 1, 2, '-');
      command.addEdit(e);
-     e = command.new Edit(Action.INSERT_GAP, seqs, 4, 1, '-');
+     e = command.new Edit(Action.INSERT_GAP, sqs, 4, 1, '-');
      command.addEdit(e);
-     e = command.new Edit(Action.INSERT_GAP, seqs, 0, 2, '-');
+     e = command.new Edit(Action.INSERT_GAP, sqs, 0, 2, '-');
      command.addEdit(e);
  
      Map<SequenceI, SequenceI> unwound = command.priorState(false);
      SequenceI seq = new Sequence("", "ABC");
      SequenceI ds = new Sequence("", "ABC");
      seq.setDatasetSequence(ds);
-     SequenceI[] seqs = new SequenceI[] { seq };
-     Edit e = command.new Edit(Action.DELETE_GAP, seqs, 1, 1, '-');
+     SequenceI[] sqs = new SequenceI[] { seq };
+     Edit e = command.new Edit(Action.DELETE_GAP, sqs, 1, 1, '-');
      command.addEdit(e);
-     e = command.new Edit(Action.DELETE_GAP, seqs, 2, 1, '-');
+     e = command.new Edit(Action.DELETE_GAP, sqs, 2, 1, '-');
      command.addEdit(e);
  
      Map<SequenceI, SequenceI> unwound = command.priorState(false);
      SequenceI seq = new Sequence("", "ABCDEF");
      SequenceI ds = new Sequence("", "ABCDEF");
      seq.setDatasetSequence(ds);
-     SequenceI[] seqs = new SequenceI[] { seq };
-     Edit e = command.new Edit(Action.DELETE_GAP, seqs, 2, 2, '-');
+     SequenceI[] sqs = new SequenceI[] { seq };
+     Edit e = command.new Edit(Action.DELETE_GAP, sqs, 2, 2, '-');
      command.addEdit(e);
  
      Map<SequenceI, SequenceI> unwound = command.priorState(false);
      SequenceI seq = new Sequence("", "AB---CDEF");
      SequenceI ds = new Sequence("", "ABCDEF");
      seq.setDatasetSequence(ds);
-     SequenceI[] seqs = new SequenceI[] { seq };
-     Edit e = command.new Edit(Action.INSERT_GAP, seqs, 2, 3, '-');
+     SequenceI[] sqs = new SequenceI[] { seq };
+     Edit e = command.new Edit(Action.INSERT_GAP, sqs, 2, 3, '-');
      command.addEdit(e);
  
      Map<SequenceI, SequenceI> unwound = command.priorState(false);
-     assertEquals("ABCDEF", unwound.get(ds).getSequenceAsString());
+     SequenceI prior = unwound.get(ds);
+     assertEquals("ABCDEF", prior.getSequenceAsString());
+     assertEquals(1, prior.getStart());
+     assertEquals(6, prior.getEnd());
+   }
+   /**
+    * Test 'undoing' a single gap insertion edit command, on a sequence whose
+    * start residue is other than 1
+    */
+   @Test(groups = { "Functional" })
+   public void testPriorState_singleInsertWithOffset()
+   {
+     EditCommand command = new EditCommand();
+     SequenceI seq = new Sequence("", "AB---CDEF", 8, 13);
+     // SequenceI ds = new Sequence("", "ABCDEF", 8, 13);
+     // seq.setDatasetSequence(ds);
+     seq.createDatasetSequence();
+     SequenceI[] sqs = new SequenceI[] { seq };
+     Edit e = command.new Edit(Action.INSERT_GAP, sqs, 2, 3, '-');
+     command.addEdit(e);
+     Map<SequenceI, SequenceI> unwound = command.priorState(false);
+     SequenceI prior = unwound.get(seq.getDatasetSequence());
+     assertEquals("ABCDEF", prior.getSequenceAsString());
+     assertEquals(8, prior.getStart());
+     assertEquals(13, prior.getEnd());
    }
  
    /**
      SequenceI seq = new Sequence("", "ABC-DEF");
      SequenceI ds1 = new Sequence("", "ABCDEF");
      seq.setDatasetSequence(ds1);
-     SequenceI[] seqs = new SequenceI[] { seq };
-     Edit e = command.new Edit(Action.DELETE_GAP, seqs, 0, 2, '-');
+     SequenceI[] sqs = new SequenceI[] { seq };
+     Edit e = command.new Edit(Action.DELETE_GAP, sqs, 0, 2, '-');
      command.addEdit(e);
      seq = new Sequence("", "ABCDEF");
      seq.setDatasetSequence(ds1);
-     seqs = new SequenceI[] { seq };
-     e = command.new Edit(Action.DELETE_GAP, seqs, 3, 1, '-');
+     sqs = new SequenceI[] { seq };
+     e = command.new Edit(Action.DELETE_GAP, sqs, 3, 1, '-');
      command.addEdit(e);
  
      /*
      seq = new Sequence("", "FGHI--J");
      SequenceI ds2 = new Sequence("", "FGHIJ");
      seq.setDatasetSequence(ds2);
-     seqs = new SequenceI[] { seq };
-     e = command.new Edit(Action.DELETE_GAP, seqs, 2, 1, '-');
+     sqs = new SequenceI[] { seq };
+     e = command.new Edit(Action.DELETE_GAP, sqs, 2, 1, '-');
      command.addEdit(e);
      seq = new Sequence("", "FGHIJ");
      seq.setDatasetSequence(ds2);
-     seqs = new SequenceI[] { seq };
-     e = command.new Edit(Action.DELETE_GAP, seqs, 4, 2, '-');
+     sqs = new SequenceI[] { seq };
+     e = command.new Edit(Action.DELETE_GAP, sqs, 4, 2, '-');
      command.addEdit(e);
  
      /*
      seq = new Sequence("", "MNOPQ");
      SequenceI ds3 = new Sequence("", "MNOPQ");
      seq.setDatasetSequence(ds3);
-     seqs = new SequenceI[] { seq };
-     e = command.new Edit(Action.DELETE_GAP, seqs, 1, 1, '-');
+     sqs = new SequenceI[] { seq };
+     e = command.new Edit(Action.DELETE_GAP, sqs, 1, 1, '-');
      command.addEdit(e);
  
      Map<SequenceI, SequenceI> unwound = command.priorState(false);
      SequenceI seq3 = new Sequence("", "M-NO--PQ");
      SequenceI ds3 = new Sequence("", "MNOPQ");
      seq3.setDatasetSequence(ds3);
-     SequenceI[] seqs = new SequenceI[] { seq1, seq2, seq3 };
-     Edit e = command.new Edit(Action.DELETE_GAP, seqs, 0, 1, '-');
+     SequenceI[] sqs = new SequenceI[] { seq1, seq2, seq3 };
+     Edit e = command.new Edit(Action.DELETE_GAP, sqs, 0, 1, '-');
      command.addEdit(e);
  
      /*
      seq2.setDatasetSequence(ds2);
      seq3 = new Sequence("", "M-NOPQ");
      seq3.setDatasetSequence(ds3);
-     seqs = new SequenceI[] { seq1, seq2, seq3 };
-     e = command.new Edit(Action.DELETE_GAP, seqs, 4, 2, '-');
+     sqs = new SequenceI[] { seq1, seq2, seq3 };
+     e = command.new Edit(Action.DELETE_GAP, sqs, 4, 2, '-');
      command.addEdit(e);
  
      Map<SequenceI, SequenceI> unwound = command.priorState(false);
@@@ -1,6 -1,6 +1,6 @@@
  /*
 - * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
 - * Copyright (C) $$Year-Rel$$ The Jalview Authors
 + * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
 + * Copyright (C) 2015 The Jalview Authors
   * 
   * This file is part of Jalview.
   * 
@@@ -150,4 -150,30 +150,30 @@@ public class AlignedCodonFrameTes
      assertEquals("[C, T, T]", Arrays.toString(acf.getMappedCodon(
              aseq1.getDatasetSequence(), 2)));
    }
+   /**
+    * Test for the case where sequences have start > 1
+    */
+   @Test(groups = { "Functional" })
+   public void testGetMappedCodon_forSubSequences()
+   {
+     final Sequence seq1 = new Sequence("Seq1", "c-G-TA-gC-gT-T", 27, 35);
+     seq1.createDatasetSequence();
+     final Sequence aseq1 = new Sequence("Seq1", "-P-R", 12, 13);
+     aseq1.createDatasetSequence();
+     /*
+      * Set up the mappings for the exons (upper-case bases)
+      */
+     AlignedCodonFrame acf = new AlignedCodonFrame();
+     MapList map = new MapList(new int[] { 28, 30, 32, 32, 34, 35 },
+             new int[] { 12, 13 }, 3, 1);
+     acf.addMap(seq1.getDatasetSequence(), aseq1.getDatasetSequence(), map);
+     assertEquals("[G, T, A]", Arrays.toString(acf.getMappedCodon(
+             aseq1.getDatasetSequence(), 12)));
+     assertEquals("[C, T, T]", Arrays.toString(acf.getMappedCodon(
+             aseq1.getDatasetSequence(), 13)));
+   }
  }
@@@ -1,6 -1,6 +1,6 @@@
  /*
 - * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
 - * Copyright (C) $$Year-Rel$$ The Jalview Authors
 + * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
 + * Copyright (C) 2015 The Jalview Authors
   * 
   * This file is part of Jalview.
   * 
@@@ -150,4 -150,31 +150,31 @@@ public class AlignedCodonIteratorTes
      assertEquals("Q", codon.product);
      assertFalse(codons.hasNext());
    }
+   /**
+    * Test for a case with sequence (and mappings) not starting at 1
+    */
+   @Test(groups = { "Functional" })
+   public void testNext_withOffset()
+   {
+     SequenceI from = new Sequence("Seq1", "-CgC-C-cCtAG-AtG-Gc", 7, 20);
+     from.createDatasetSequence();
+     SequenceI to = new Sequence("Seq1/10-12", "-PQ-R-");
+     to.createDatasetSequence();
+     MapList map = new MapList(new int[] { 7, 7, 9, 10, 12, 12, 14, 16, 18,
+         19 }, new int[] { 10, 12 }, 3, 1);
+     Mapping m = new Mapping(to.getDatasetSequence(), map);
+     Iterator<AlignedCodon> codons = m.getCodonIterator(from, '-');
+     AlignedCodon codon = codons.next();
+     assertEquals("[1, 3, 5]", codon.toString());
+     assertEquals("P", codon.product);
+     codon = codons.next();
+     assertEquals("[8, 10, 11]", codon.toString());
+     assertEquals("Q", codon.product);
+     codon = codons.next();
+     assertEquals("[13, 15, 17]", codon.toString());
+     assertEquals("R", codon.product);
+     assertFalse(codons.hasNext());
+   }
  }
@@@ -1,6 -1,6 +1,6 @@@
  /*
 - * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
 - * Copyright (C) $$Year-Rel$$ The Jalview Authors
 + * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
 + * Copyright (C) 2015 The Jalview Authors
   * 
   * This file is part of Jalview.
   * 
@@@ -57,21 -57,21 +57,21 @@@ public class AlignmentTes
            "//";
  
    private static final String AA_SEQS_1 = 
-           ">Seq1Name\n" +
+           ">Seq1Name/5-8\n" +
            "K-QY--L\n" +
-           ">Seq2Name\n" +
+           ">Seq2Name/12-15\n" +
            "-R-FP-W-\n";
  
    private static final String CDNA_SEQS_1 = 
-           ">Seq1Name\n" +
+           ">Seq1Name/100-111\n" +
            "AC-GG--CUC-CAA-CT\n" +
-           ">Seq2Name\n" +
+           ">Seq2Name/200-211\n" +
            "-CG-TTA--ACG---AAGT\n";
  
    private static final String CDNA_SEQS_2 = 
-           ">Seq1Name\n" +
+           ">Seq1Name/50-61\n" +
            "GCTCGUCGTACT\n" +
-           ">Seq2Name\n" +
+           ">Seq2Name/60-71\n" +
            "GGGTCAGGCAGT\n";
    // @formatter:on
  
       * Make mappings between sequences. The 'aligned cDNA' is playing the role
       * of what would normally be protein here.
       */
-     AlignedCodonFrame acf = new AlignedCodonFrame();
-     MapList ml = new MapList(new int[] { 1, 12 }, new int[] { 1, 12 }, 1, 1);
-     acf.addMap(al2.getSequenceAt(0), al1.getSequenceAt(0), ml);
-     acf.addMap(al2.getSequenceAt(1), al1.getSequenceAt(1), ml);
-     al1.addCodonFrame(acf);
+     makeMappings(al2, al1);
  
      ((Alignment) al2).alignAs(al1, false, true);
      assertEquals("GC-TC--GUC-GTA-CT", al2.getSequenceAt(0)
      // see also AlignmentUtilsTests
      AlignmentI al1 = loadAlignment(CDNA_SEQS_1, "FASTA");
      AlignmentI al2 = loadAlignment(AA_SEQS_1, "FASTA");
-     AlignedCodonFrame acf = new AlignedCodonFrame();
-     MapList ml = new MapList(new int[] { 1, 12 }, new int[] { 1, 4 }, 3, 1);
-     acf.addMap(al1.getSequenceAt(0), al2.getSequenceAt(0), ml);
-     acf.addMap(al1.getSequenceAt(1), al2.getSequenceAt(1), ml);
-     al2.addCodonFrame(acf);
+     makeMappings(al1, al2);
  
      ((Alignment) al2).alignAs(al1, false, true);
      assertEquals("K-Q-Y-L-", al2.getSequenceAt(0).getSequenceAsString());
    }
  
    /**
+    * Aligning protein from cDNA for a single sequence. This is the 'simple' case
+    * (as there is no need to compute codon 'alignments') but worth testing
+    * before tackling the multiple sequence case.
+    * 
+    * @throws IOException
+    */
+   @Test(groups = { "Functional" })
+   public void testAlignAs_proteinAsCdna_singleSequence() throws IOException
+   {
+     /*
+      * simplest case remove all gaps
+      */
+     verifyAlignAs(">protein\n-Q-K-\n", ">dna\nCAAaaa\n", "QK");
+     /*
+      * with sequence offsets
+      */
+     verifyAlignAs(">protein/12-13\n-Q-K-\n", ">dna/20-25\nCAAaaa\n", "QK");
+   }
+   /**
     * Test aligning cdna as per protein alignment.
     * 
     * @throws IOException
       */
      AlignmentI al1 = loadAlignment(CDNA_SEQS_1, "FASTA");
      AlignmentI al2 = loadAlignment(AA_SEQS_1, "FASTA");
-     AlignedCodonFrame acf = new AlignedCodonFrame();
-     MapList ml = new MapList(new int[] { 1, 12 }, new int[] { 1, 4 }, 3, 1);
-     acf.addMap(al1.getSequenceAt(0), al2.getSequenceAt(0), ml);
-     acf.addMap(al1.getSequenceAt(1), al2.getSequenceAt(1), ml);
-     al2.addCodonFrame(acf);
+     makeMappings(al1, al2);
  
      /*
       * Realign DNA; currently keeping existing gaps in introns only
    }
  
    /**
+    * Test aligning cdna as per protein - single sequences
+    * 
+    * @throws IOException
+    */
+   @Test(groups = { "Functional" })
+   public void testAlignAs_cdnaAsProtein_singleSequence() throws IOException
+   {
+     /*
+      * simple case insert one gap
+      */
+     verifyAlignAs(">dna\nCAAaaa\n", ">protein\nQ-K\n", "CAA---aaa");
+     /*
+      * simple case but with sequence offsets
+      */
+     verifyAlignAs(">dna/5-10\nCAAaaa\n", ">protein/20-21\nQ-K\n",
+             "CAA---aaa");
+     /*
+      * insert gaps as per protein, drop gaps within codons
+      */
+     verifyAlignAs(">dna/10-18\nCA-Aa-aa--AGA\n", ">aa/6-8\n-Q-K--R\n",
+             "---CAA---aaa------AGA");
+   }
+   /**
+    * Helper method that makes mappings and then aligns the first alignment as
+    * the second
+    * 
+    * @param fromSeqs
+    * @param toSeqs
+    * @param expected
+    * @throws IOException
+    */
+   public void verifyAlignAs(String fromSeqs, String toSeqs, String expected)
+           throws IOException
+   {
+     /*
+      * Load alignments and add mappings from nucleotide to protein (or from
+      * first to second if both the same type)
+      */
+     AlignmentI al1 = loadAlignment(fromSeqs, "FASTA");
+     AlignmentI al2 = loadAlignment(toSeqs, "FASTA");
+     makeMappings(al1, al2);
+     /*
+      * Realign DNA; currently keeping existing gaps in introns only
+      */
+     ((Alignment) al1).alignAs(al2, false, true);
+     assertEquals(expected, al1.getSequenceAt(0).getSequenceAsString());
+   }
+   /**
+    * Helper method to make mappings from protein to dna sequences, and add the
+    * mappings to the protein alignment
+    * 
+    * @param alFrom
+    * @param alTo
+    */
+   public void makeMappings(AlignmentI alFrom, AlignmentI alTo)
+   {
+     AlignmentI prot = !alFrom.isNucleotide() ? alFrom : alTo;
+     AlignmentI nuc = alFrom == prot ? alTo : alFrom;
+     int ratio = (alFrom.isNucleotide() == alTo.isNucleotide() ? 1 : 3);
+     AlignedCodonFrame acf = new AlignedCodonFrame();
+     for (int i = 0; i < nuc.getHeight(); i++)
+     {
+       SequenceI seqFrom = nuc.getSequenceAt(i);
+       SequenceI seqTo = prot.getSequenceAt(i);
+       MapList ml = new MapList(new int[] { seqFrom.getStart(),
+           seqFrom.getEnd() },
+               new int[] { seqTo.getStart(), seqTo.getEnd() }, ratio, 1);
+       acf.addMap(seqFrom, seqTo, ml);
+     }
+     prot.addCodonFrame(acf);
+   }
+   /**
     * Test aligning dna as per protein alignment, for the case where there are
     * introns (i.e. some dna sites have no mapping from a peptide).
     * 
       */
      String dna1 = "A-Aa-gG-GCC-cT-TT";
      String dna2 = "c--CCGgg-TT--T-AA-A";
-     AlignmentI al1 = loadAlignment(">Seq1\n" + dna1 + "\n>Seq2\n" + dna2
-             + "\n", "FASTA");
-     AlignmentI al2 = loadAlignment(">Seq1\n-P--YK\n>Seq2\nG-T--F\n",
-             "FASTA");
+     AlignmentI al1 = loadAlignment(">Seq1/6-17\n" + dna1
+             + "\n>Seq2/20-31\n" + dna2 + "\n", "FASTA");
+     AlignmentI al2 = loadAlignment(
+             ">Seq1/7-9\n-P--YK\n>Seq2/11-13\nG-T--F\n", "FASTA");
      AlignedCodonFrame acf = new AlignedCodonFrame();
      // Seq1 has intron at dna positions 3,4,9 so splice is AAG GCC TTT
      // Seq2 has intron at dna positions 1,5,6 so splice is CCG TTT AAA
-     MapList ml1 = new MapList(new int[] { 1, 2, 5, 8, 10, 12 }, new int[] {
-         1, 3 }, 3, 1);
+     // TODO sequence offsets
+     MapList ml1 = new MapList(new int[] { 6, 7, 10, 13, 15, 17 }, new int[]
+     { 7, 9 }, 3, 1);
      acf.addMap(al1.getSequenceAt(0), al2.getSequenceAt(0), ml1);
-     MapList ml2 = new MapList(new int[] { 2, 4, 7, 12 },
-             new int[] { 1, 3 }, 3, 1);
+     MapList ml2 = new MapList(new int[] { 21, 23, 26, 31 }, new int[] { 11,
+         13 }, 3, 1);
      acf.addMap(al1.getSequenceAt(1), al2.getSequenceAt(1), ml2);
      al2.addCodonFrame(acf);
  
@@@ -1,6 -1,6 +1,6 @@@
  /*
 - * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
 - * Copyright (C) $$Year-Rel$$ The Jalview Authors
 + * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
 + * Copyright (C) 2015 The Jalview Authors
   * 
   * This file is part of Jalview.
   * 
@@@ -20,6 -20,7 +20,7 @@@
   */
  package jalview.io;
  
+ import static org.testng.AssertJUnit.assertFalse;
  import static org.testng.AssertJUnit.assertTrue;
  
  import jalview.api.AlignmentViewPanel;
@@@ -412,4 -413,56 +413,56 @@@ public class Jalview2xmlTest
              Desktop.getAlignmentPanels(af.getViewport().getSequenceSetId()).length);
    }
  
+   @Test(groups = { "Functional" })
+   public void testIsVersionStringLaterThan()
+   {
+     /*
+      * No version / development / test / autobuild is leniently assumed to be
+      * compatible
+      */
+     assertTrue(Jalview2XML.isVersionStringLaterThan(null, null));
+     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3", null));
+     assertTrue(Jalview2XML.isVersionStringLaterThan(null,
+             "Development Build"));
+     assertTrue(Jalview2XML.isVersionStringLaterThan(null,
+             "DEVELOPMENT BUILD"));
+     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3",
+             "Development Build"));
+     assertTrue(Jalview2XML.isVersionStringLaterThan(null, "Test"));
+     assertTrue(Jalview2XML.isVersionStringLaterThan(null, "TEST"));
+     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3", "Test"));
+     assertTrue(Jalview2XML
+             .isVersionStringLaterThan(null, "Automated Build"));
+     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3",
+             "Automated Build"));
+     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3",
+             "AUTOMATED BUILD"));
+     /*
+      * same version returns true i.e. compatible
+      */
+     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8", "2.8"));
+     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3", "2.8.3"));
+     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3b1", "2.8.3b1"));
+     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3B1", "2.8.3b1"));
+     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3b1", "2.8.3B1"));
+     /*
+      * later version returns true
+      */
+     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3", "2.8.4"));
+     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3", "2.9"));
+     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3", "2.9.2"));
+     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8", "2.8.3"));
+     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3", "2.8.3b1"));
+     /*
+      * earlier version returns false
+      */
+     assertFalse(Jalview2XML.isVersionStringLaterThan("2.8.3", "2.8"));
+     assertFalse(Jalview2XML.isVersionStringLaterThan("2.8.4", "2.8.3"));
+     assertFalse(Jalview2XML.isVersionStringLaterThan("2.8.3b1", "2.8.3"));
+     assertFalse(Jalview2XML.isVersionStringLaterThan("2.8.3", "2.8.2b1"));
+     assertFalse(Jalview2XML.isVersionStringLaterThan("2.8.0b2", "2.8.0b1"));
+   }
  }
@@@ -1,6 -1,6 +1,6 @@@
  /*
 - * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
 - * Copyright (C) $$Year-Rel$$ The Jalview Authors
 + * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
 + * Copyright (C) 2015 The Jalview Authors
   * 
   * This file is part of Jalview.
   * 
@@@ -25,7 -25,11 +25,11 @@@ import static org.testng.AssertJUnit.as
  import static org.testng.AssertJUnit.assertTrue;
  
  import jalview.api.AlignViewportI;
+ import jalview.commands.EditCommand;
+ import jalview.commands.EditCommand.Action;
+ import jalview.commands.EditCommand.Edit;
  import jalview.datamodel.AlignedCodonFrame;
+ import jalview.datamodel.Alignment;
  import jalview.datamodel.AlignmentI;
  import jalview.datamodel.ColumnSelection;
  import jalview.datamodel.SearchResults;
@@@ -42,6 -46,7 +46,7 @@@ import java.io.IOException
  import java.util.Arrays;
  import java.util.Collections;
  import java.util.HashSet;
+ import java.util.LinkedHashSet;
  import java.util.List;
  import java.util.Set;
  
@@@ -59,46 -64,47 +64,47 @@@ public class MappingUtilsTes
    @Test(groups = { "Functional" })
    public void testBuildSearchResults()
    {
-     final Sequence seq1 = new Sequence("Seq1", "C-G-TA-GC");
+     final Sequence seq1 = new Sequence("Seq1/5-10", "C-G-TA-GC");
      seq1.createDatasetSequence();
  
-     final Sequence aseq1 = new Sequence("Seq1", "-P-R");
+     final Sequence aseq1 = new Sequence("Seq1/12-13", "-P-R");
      aseq1.createDatasetSequence();
  
      /*
-      * Map dna bases 1-6 to protein residues 1-2
+      * Map dna bases 5-10 to protein residues 12-13
       */
      AlignedCodonFrame acf = new AlignedCodonFrame();
-     MapList map = new MapList(new int[] { 1, 6 }, new int[] { 1, 2 }, 3, 1);
+     MapList map = new MapList(new int[] { 5, 10 }, new int[] { 12, 13 }, 3,
+             1);
      acf.addMap(seq1.getDatasetSequence(), aseq1.getDatasetSequence(), map);
      Set<AlignedCodonFrame> acfList = Collections.singleton(acf);
  
      /*
-      * Check protein residue 1 maps to codon 1-3, 2 to codon 4-6
+      * Check protein residue 12 maps to codon 5-7, 13 to codon 8-10
       */
-     SearchResults sr = MappingUtils.buildSearchResults(aseq1, 1, acfList);
+     SearchResults sr = MappingUtils.buildSearchResults(aseq1, 12, acfList);
      assertEquals(1, sr.getResults().size());
      Match m = sr.getResults().get(0);
      assertEquals(seq1.getDatasetSequence(), m.getSequence());
-     assertEquals(1, m.getStart());
-     assertEquals(3, m.getEnd());
-     sr = MappingUtils.buildSearchResults(aseq1, 2, acfList);
+     assertEquals(5, m.getStart());
+     assertEquals(7, m.getEnd());
+     sr = MappingUtils.buildSearchResults(aseq1, 13, acfList);
      assertEquals(1, sr.getResults().size());
      m = sr.getResults().get(0);
      assertEquals(seq1.getDatasetSequence(), m.getSequence());
-     assertEquals(4, m.getStart());
-     assertEquals(6, m.getEnd());
+     assertEquals(8, m.getStart());
+     assertEquals(10, m.getEnd());
  
      /*
-      * Check inverse mappings, from codons 1-3, 4-6 to protein 1, 2
+      * Check inverse mappings, from codons 5-7, 8-10 to protein 12, 13
       */
-     for (int i = 1; i < 7; i++)
+     for (int i = 5; i < 11; i++)
      {
        sr = MappingUtils.buildSearchResults(seq1, i, acfList);
        assertEquals(1, sr.getResults().size());
        m = sr.getResults().get(0);
        assertEquals(aseq1.getDatasetSequence(), m.getSequence());
-       int residue = i > 3 ? 2 : 1;
+       int residue = i > 7 ? 13 : 12;
        assertEquals(residue, m.getStart());
        assertEquals(residue, m.getEnd());
      }
    @Test(groups = { "Functional" })
    public void testBuildSearchResults_withIntron()
    {
-     final Sequence seq1 = new Sequence("Seq1", "C-G-TAGA-GCAGCTT");
+     final Sequence seq1 = new Sequence("Seq1/5-17", "c-G-tAGa-GcAgCtt");
      seq1.createDatasetSequence();
  
-     final Sequence aseq1 = new Sequence("Seq1", "-P-R");
+     final Sequence aseq1 = new Sequence("Seq1/8-9", "-E-D");
      aseq1.createDatasetSequence();
  
      /*
-      * Map dna bases [2, 4, 5], [7, 9, 11] to protein residues 1 and 2
+      * Map dna bases [6, 8, 9], [11, 13, 115] to protein residues 8 and 9
       */
      AlignedCodonFrame acf = new AlignedCodonFrame();
-     MapList map = new MapList(new int[] { 2, 2, 4, 5, 7, 7, 9, 9, 11, 11 },
-             new int[] { 1, 2 }, 3, 1);
+     MapList map = new MapList(new int[] { 6, 6, 8, 9, 11, 11, 13, 13, 15,
+         15 }, new int[] { 8, 9 }, 3, 1);
      acf.addMap(seq1.getDatasetSequence(), aseq1.getDatasetSequence(), map);
      Set<AlignedCodonFrame> acfList = Collections.singleton(acf);
  
      /*
-      * Check protein residue 1 maps to [2, 4, 5]
+      * Check protein residue 8 maps to [6, 8, 9]
       */
-     SearchResults sr = MappingUtils.buildSearchResults(aseq1, 1, acfList);
+     SearchResults sr = MappingUtils.buildSearchResults(aseq1, 8, acfList);
      assertEquals(2, sr.getResults().size());
      Match m = sr.getResults().get(0);
      assertEquals(seq1.getDatasetSequence(), m.getSequence());
-     assertEquals(2, m.getStart());
-     assertEquals(2, m.getEnd());
+     assertEquals(6, m.getStart());
+     assertEquals(6, m.getEnd());
      m = sr.getResults().get(1);
      assertEquals(seq1.getDatasetSequence(), m.getSequence());
-     assertEquals(4, m.getStart());
-     assertEquals(5, m.getEnd());
+     assertEquals(8, m.getStart());
+     assertEquals(9, m.getEnd());
  
      /*
-      * Check protein residue 2 maps to [7, 9, 11]
+      * Check protein residue 9 maps to [11, 13, 15]
       */
-     sr = MappingUtils.buildSearchResults(aseq1, 2, acfList);
+     sr = MappingUtils.buildSearchResults(aseq1, 9, acfList);
      assertEquals(3, sr.getResults().size());
      m = sr.getResults().get(0);
      assertEquals(seq1.getDatasetSequence(), m.getSequence());
-     assertEquals(7, m.getStart());
-     assertEquals(7, m.getEnd());
+     assertEquals(11, m.getStart());
+     assertEquals(11, m.getEnd());
      m = sr.getResults().get(1);
      assertEquals(seq1.getDatasetSequence(), m.getSequence());
-     assertEquals(9, m.getStart());
-     assertEquals(9, m.getEnd());
+     assertEquals(13, m.getStart());
+     assertEquals(13, m.getEnd());
      m = sr.getResults().get(2);
      assertEquals(seq1.getDatasetSequence(), m.getSequence());
-     assertEquals(11, m.getStart());
-     assertEquals(11, m.getEnd());
+     assertEquals(15, m.getStart());
+     assertEquals(15, m.getEnd());
  
      /*
       * Check inverse mappings, from codons to protein
       */
-     for (int i = 1; i < 14; i++)
+     for (int i = 5; i < 18; i++)
      {
        sr = MappingUtils.buildSearchResults(seq1, i, acfList);
-       int residue = (i == 2 || i == 4 || i == 5) ? 1 : (i == 7 || i == 9
-               || i == 11 ? 2 : 0);
+       int residue = (i == 6 || i == 8 || i == 9) ? 8 : (i == 11 || i == 13
+               || i == 15 ? 9 : 0);
        if (residue == 0)
        {
          assertEquals(0, sr.getResults().size());
    }
  
    /**
+    * Set up mappings for tests from 3 dna to 3 protein sequences. Sequences have
+    * offset start positions for a more general test case.
+    * 
     * @throws IOException
     */
    protected void setupMappedAlignments() throws IOException
       * Set up dna and protein Seq1/2/3 with mappings (held on the protein
       * viewport). Lower case for introns.
       */
-     AlignmentI cdna = loadAlignment(">Seq1\nAC-GctGtC-T\n"
-             + ">Seq2\nTc-GA-G-T-Tc\n" + ">Seq3\nTtTT-AaCGg-\n", "FASTA");
+     AlignmentI cdna = loadAlignment(">Seq1/10-18\nAC-GctGtC-T\n"
+             + ">Seq2/20-27\nTc-GA-G-T-Tc\n" + ">Seq3/30-38\nTtTT-AaCGg-\n",
+             "FASTA");
      cdna.setDataset(null);
      AlignmentI protein = loadAlignment(
-             ">Seq1\n-K-P\n>Seq2\nL--Q\n>Seq3\nG--S\n", "FASTA");
+             ">Seq1/40-41\n-K-P\n>Seq2/50-51\nL--Q\n>Seq3/60-61\nG--S\n",
+             "FASTA");
      protein.setDataset(null);
+     // map first dna to first protein seq
      AlignedCodonFrame acf = new AlignedCodonFrame();
-     MapList map = new MapList(new int[] { 1, 3, 6, 6, 8, 9 }, new int[] {
-         1, 2 }, 3, 1);
+     MapList map = new MapList(new int[] { 10, 12, 15, 15, 17, 18 },
+             new int[] { 40, 41 }, 3, 1);
      acf.addMap(cdna.getSequenceAt(0).getDatasetSequence(), protein
              .getSequenceAt(0).getDatasetSequence(), map);
-     map = new MapList(new int[] { 1, 1, 3, 4, 5, 7 }, new int[] { 1, 2 },
+     // map second dna to second protein seq
+     map = new MapList(new int[] { 20, 20, 22, 23, 24, 26 }, new int[] { 50,
+         51 },
              3, 1);
      acf.addMap(cdna.getSequenceAt(1).getDatasetSequence(), protein
              .getSequenceAt(1).getDatasetSequence(), map);
-     map = new MapList(new int[] { 1, 1, 3, 4, 5, 5, 7, 8 }, new int[] { 1,
-         2 }, 3, 1);
+     // map third dna to third protein seq
+     map = new MapList(new int[] { 30, 30, 32, 34, 36, 37 }, new int[] { 60,
+         61 }, 3, 1);
      acf.addMap(cdna.getSequenceAt(2).getDatasetSequence(), protein
              .getSequenceAt(2).getDatasetSequence(), map);
      Set<AlignedCodonFrame> acfList = Collections.singleton(acf);
      result = MappingUtils.findMappingsForSequence(null, null);
      assertEquals(0, result.size());
    }
+   @Test(groups = { "Functional" })
+   public void testMapEditCommand()
+   {
+     SequenceI dna = new Sequence("Seq1", "---ACG---GCATCA", 8, 16);
+     SequenceI protein = new Sequence("Seq2", "-T-AS", 5, 7);
+     dna.createDatasetSequence();
+     protein.createDatasetSequence();
+     AlignedCodonFrame acf = new AlignedCodonFrame();
+     MapList map = new MapList(new int[] { 8, 16 }, new int[] { 5, 7 }, 3, 1);
+     acf.addMap(dna.getDatasetSequence(), protein.getDatasetSequence(), map);
+     Set<AlignedCodonFrame> mappings = new LinkedHashSet<AlignedCodonFrame>();
+     mappings.add(acf);
+     AlignmentI prot = new Alignment(new SequenceI[] { protein });
+     prot.setCodonFrames(mappings);
+     AlignmentI nuc = new Alignment(new SequenceI[] { dna });
+     /*
+      * construct and perform the edit command to turn "-T-AS" in to "-T-A--S"
+      * i.e. insert two gaps at column 4
+      */
+     EditCommand ec = new EditCommand();
+     final Edit edit = ec.new Edit(Action.INSERT_GAP,
+             new SequenceI[] { protein }, 4, 2, '-');
+     ec.appendEdit(edit, prot, true, null);
+     /*
+      * the mapped edit command should be to insert 6 gaps before base 4 in the
+      * nucleotide sequence, which corresponds to aligned column 12 in the dna
+      */
+     EditCommand mappedEdit = MappingUtils.mapEditCommand(ec, false, nuc,
+             '-', mappings);
+     assertEquals(1, mappedEdit.getEdits().size());
+     Edit e = mappedEdit.getEdits().get(0);
+     assertEquals(1, e.getSequences().length);
+     assertEquals(dna, e.getSequences()[0]);
+     assertEquals(12, e.getPosition());
+     assertEquals(6, e.getNumber());
+   }
  }