001/**
002 *                  BioJava development code
003 *
004 * This code may be freely distributed and modified under the
005 * terms of the GNU Lesser General Public Licence.  This should
006 * be distributed with the code.  If you do not have a copy,
007 * see:
008 *
009 *      http://www.gnu.org/copyleft/lesser.html
010 *
011 * Copyright for this code is held jointly by the individual
012 * authors.  These should be listed in @author doc comments.
013 *
014 * For more information on the BioJava project and its aims,
015 * or to join the biojava-l mailing list, visit the home page
016 * at:
017 *
018 *      http://www.biojava.org/
019 *
020 * Author: Andreas Prlic
021 *
022 *
023 */
024
025package org.biojava.nbio.structure.scop;
026
027import org.biojava.nbio.structure.Structure;
028import org.biojava.nbio.structure.StructureTools;
029import org.biojava.nbio.structure.align.util.UserConfiguration;
030import org.biojava.nbio.core.util.FileDownloadUtils;
031import org.biojava.nbio.core.util.InputStreamProvider;
032import org.slf4j.Logger;
033import org.slf4j.LoggerFactory;
034
035import java.io.*;
036import java.net.URL;
037import java.util.*;
038import java.util.concurrent.atomic.AtomicBoolean;
039
040
041/** This class provides access to the SCOP protein structure classification.
042 *
043 * For more information about SCOP see here:
044 *  <ul>
045 *   <li>SCOP: <a href="http://scop.mrc-lmb.cam.ac.uk/scop/">http://scop.mrc-lmb.cam.ac.uk/scop/</a></li>
046
047 *  <li> Introduction: <a href="http://scop.mrc-lmb.cam.ac.uk/scop/intro.html">http://scop.mrc-lmb.cam.ac.uk/scop/intro.html</a> </li>
048
049 *   <li> SCOP parsable files: <a href="http://scop.mrc-lmb.cam.ac.uk/scop/parse/">http://scop.mrc-lmb.cam.ac.uk/scop/parse/</a> </li>
050 * </ul>
051
052 *
053 * This class can automatically download missing files from the SCOP classification.
054 *
055 * @author Andreas Prlic
056 *
057 */
058public class ScopInstallation implements LocalScopDatabase {
059
060        public static final String DEFAULT_VERSION = "1.75";
061
062        private static final Logger logger = LoggerFactory.getLogger(ScopInstallation.class);
063
064        protected String scopVersion;
065
066        // Stores URLs for cla, des, hie, and com files
067        private final List<ScopMirror> mirrors;
068
069        // Cache filenames (with version appended)
070        public static final String claFileName = "dir.cla.scop.txt_";
071        public static final String desFileName = "dir.des.scop.txt_";
072        public static final String hieFileName = "dir.hie.scop.txt_";
073        public static final String comFileName = "dir.com.scop.txt_";
074
075        // Download locations
076        public static final String SCOP_DOWNLOAD = "http://scop.berkeley.edu/downloads/parse/";
077        public static final String SCOP_DOWNLOAD_ALTERNATE = "http://scop.berkeley.edu/downloads/parse/";
078
079        public static final String NEWLINE;
080        public static final String FILESPLIT ;
081
082        static {
083
084                NEWLINE     = System.getProperty("line.separator");
085                FILESPLIT   = System.getProperty("file.separator");
086        }
087
088        String cacheLocation ;
089
090        AtomicBoolean installedCla;
091        AtomicBoolean installedDes;
092        AtomicBoolean installedHie;
093        AtomicBoolean installedCom;
094
095        Map<Integer, List<String>> commentsMap;
096        Map<String, List<ScopDomain>> domainMap;
097        Map<Integer, ScopDescription> sunidMap;
098        Map<Integer, ScopNode> scopTree;
099
100
101        /** Create a new SCOP installation.
102         *
103         * @param cacheLocation where the SCOP files are stored. If they can't be found at that location they will get automatically downloaded and installed there.
104         */
105        public ScopInstallation(String cacheLocation){
106
107                setCacheLocation(cacheLocation);
108
109                installedCla = new AtomicBoolean();
110                installedCla.set(false);
111                installedDes = new AtomicBoolean();
112                installedDes.set(false);
113                installedHie = new AtomicBoolean();
114                installedHie.set(false);
115                installedCom = new AtomicBoolean();
116                installedCom.set(false);
117
118                scopVersion = DEFAULT_VERSION;
119                mirrors = new ArrayList<ScopMirror>(1);
120
121                domainMap = new HashMap<String, List<ScopDomain>>();
122
123                sunidMap  = new HashMap<Integer, ScopDescription>();
124                scopTree  = new TreeMap<Integer, ScopNode>();
125
126        }
127
128        /**
129         * Removes all of the comments (dir.com file) in order to free memory. The file will need to be reloaded if {@link #getComments(int)} is called subsequently.
130         */
131        public void nullifyComments() {
132                commentsMap = null;
133                installedCom.set(false);
134        }
135
136        /**
137         * Create a new SCOP installation, downloading the file to "the right place".
138         * This will first check for system properties or environmental variables
139         * called {@link UserConfiguration#PDB_CACHE_DIR}, or else will use a temporary directory
140         */
141        public ScopInstallation() {
142                this((new UserConfiguration()).getCacheFilePath());
143        }
144
145        public void ensureClaInstalled() throws IOException {
146                if (installedCla.get()) return;
147                if (!claFileAvailable()) downloadClaFile();
148                parseClassification();
149                installedCla.set(true);
150        }
151
152        public void ensureDesInstalled() throws IOException {
153                if (installedDes.get()) return;
154                if (!desFileAvailable()) downloadDesFile();
155                parseDescriptions();
156                installedDes.set(true);
157        }
158
159        public void ensureComInstalled() throws IOException {
160                if (installedCom.get()) return;
161                if (!comFileAvailable()) downloadComFile();
162                parseComments();
163                installedCom.set(true);
164        }
165
166        public void ensureHieInstalled() throws IOException {
167                if ( installedHie.get()) return;
168                if ( ! hieFileAvailable()) downloadHieFile();
169                parseHierarchy();
170                installedHie.set(true);
171        }
172
173        /* (non-Javadoc)
174         * @see org.biojava.nbio.structure.scop.ScopDatabase#getByCategory(org.biojava.nbio.structure.scop.ScopCategory)
175         */
176        @Override
177        public List<ScopDescription> getByCategory(ScopCategory category){
178
179                try {
180                        ensureDesInstalled();
181                } catch (IOException e) {
182                        throw new ScopIOException(e);
183                }
184
185                List<ScopDescription> matches = new ArrayList<ScopDescription>();
186                for (Integer i : sunidMap.keySet()){
187                        ScopDescription sc = sunidMap.get(i);
188                        if ( sc.getCategory().equals(category))
189
190                                try {
191                                        matches.add((ScopDescription)sc.clone());
192                                } catch (CloneNotSupportedException e) {
193                                        throw new RuntimeException("Could not clone " + ScopDescription.class + " subclass", e);
194                                }
195
196                }
197                return matches;
198        }
199
200        /* (non-Javadoc)
201         * @see org.biojava.nbio.structure.scop.ScopDatabase#filterByClassificationId(java.lang.String)
202         */
203        @Override
204        public List<ScopDescription> filterByClassificationId(String query){
205
206                try {
207                        ensureDesInstalled();
208                } catch (IOException e) {
209                        throw new ScopIOException(e);
210                }
211
212                List<ScopDescription> matches = new ArrayList<ScopDescription>();
213                for (Integer i : sunidMap.keySet()){
214                        ScopDescription sc = sunidMap.get(i);
215
216
217                        if( sc.getClassificationId().startsWith(query)){
218                                matches.add(sc);
219                        }
220                }
221
222                return matches;
223        }
224
225
226        /* (non-Javadoc)
227         * @see org.biojava.nbio.structure.scop.ScopDatabase#getTree(org.biojava.nbio.structure.scop.ScopDomain)
228         */
229        @Override
230        public List<ScopNode> getTree(ScopDomain domain){
231                ScopNode node = getScopNode(domain.getSunid());
232
233
234                List<ScopNode> tree = new ArrayList<ScopNode>();
235                while (node != null){
236
237                        //System.out.println("This node: sunid:" + node.getSunid() );
238                        //System.out.println(getScopDescriptionBySunid(node.getSunid()));
239                        node = getScopNode(node.getParentSunid());
240                        if ( node != null)
241                                tree.add(node);
242                }
243                Collections.reverse(tree);
244                return tree;
245        }
246
247        /* (non-Javadoc)
248         * @see org.biojava.nbio.structure.scop.ScopDatabase#filterByDomainName(java.lang.String)
249         */
250        @Override
251        public List<ScopDomain> filterByDomainName(String query) {
252
253                List<ScopDomain > domains = new ArrayList<ScopDomain>();
254                if (query.length() <5){
255                        return domains;
256                }
257
258                String pdbId = query.substring(1,5);
259
260                List<ScopDomain> doms = getDomainsForPDB(pdbId);
261
262
263                if ( doms == null)
264                        return domains;
265
266                query = query.toLowerCase();
267                for ( ScopDomain d: doms){
268                        if ( d.getScopId().toLowerCase().contains(query)){
269                                domains.add(d);
270                        }
271                }
272
273                return domains;
274        }
275
276        /* (non-Javadoc)
277         * @see org.biojava.nbio.structure.scop.ScopDatabase#filterByDescription(java.lang.String)
278         */
279        @Override
280        public List<ScopDescription> filterByDescription(String query) throws ScopIOException {
281                try {
282                        ensureDesInstalled();
283                } catch (IOException e) {
284                        throw new ScopIOException(e);
285                }
286
287                query = query.toLowerCase();
288                List<ScopDescription> matches = new ArrayList<ScopDescription>();
289                for (Integer i : sunidMap.keySet()){
290                        ScopDescription sc = sunidMap.get(i);
291
292                        if( sc.getDescription().toLowerCase().startsWith(query)){
293                                matches.add(sc);
294                        }
295                }
296
297                return matches;
298        }
299
300
301        /* (non-Javadoc)
302         * @see org.biojava.nbio.structure.scop.ScopDatabase#getScopDescriptionBySunid(int)
303         */
304        @Override
305        public ScopDescription getScopDescriptionBySunid(int sunid) {
306                try {
307                        ensureDesInstalled();
308                } catch (IOException e) {
309                        throw new ScopIOException(e);
310                }
311                return sunidMap.get(sunid);
312        }
313
314        /* (non-Javadoc)
315         * @see org.biojava.nbio.structure.scop.ScopDatabase#getDomainsForPDB(java.lang.String)
316         */
317        @Override
318        public  List<ScopDomain> getDomainsForPDB(String pdbId) {
319
320                try {
321                        ensureClaInstalled();
322                } catch (IOException e) {
323                        throw new ScopIOException(e);
324                }
325
326                List<ScopDomain> doms = domainMap.get(pdbId.toLowerCase());
327
328                List<ScopDomain> retdoms = new ArrayList<ScopDomain>();
329
330                if ( doms == null)
331                        return retdoms;
332
333                for ( ScopDomain d : doms){
334                        try {
335                                ScopDomain n = (ScopDomain) d.clone();
336                                retdoms.add(n);
337                        }  catch (CloneNotSupportedException e){
338                                throw new RuntimeException(ScopDomain.class + " subclass does not support clone()", e);
339                        }
340
341
342                }
343                return retdoms;
344        }
345
346        /* (non-Javadoc)
347         * @see org.biojava.nbio.structure.scop.ScopDatabase#getDomainByScopID(java.lang.String)
348         */
349        @Override
350        public ScopDomain getDomainByScopID(String scopId) {
351
352                try {
353                        ensureClaInstalled();
354                } catch (IOException e) {
355                        throw new ScopIOException(e);
356                }
357
358                if ( scopId.length() < 6) {
359                        throw new ScopIOException("Does not look like a scop ID! " + scopId);
360                }
361                String pdbId = scopId.substring(1,5);
362                List<ScopDomain> doms = getDomainsForPDB(pdbId);
363                if ( doms == null)
364                        return null;
365                for ( ScopDomain d : doms){
366                        if ( d.getScopId().equalsIgnoreCase(scopId))
367                                return d;
368                }
369
370                return null;
371        }
372
373        /* (non-Javadoc)
374         * @see org.biojava.nbio.structure.scop.ScopDatabase#getScopNode(int)
375         */
376        @Override
377        public ScopNode getScopNode(int sunid){
378
379                try {
380                        ensureHieInstalled();
381                } catch (IOException e) {
382                        throw new ScopIOException(e);
383                }
384
385                return scopTree.get(sunid);
386        }
387
388
389        private void parseClassification() throws IOException{
390
391                File file = new File(getClaFilename());
392
393                InputStreamProvider ips = new InputStreamProvider();
394                BufferedReader buffer = new BufferedReader (new InputStreamReader(ips.getInputStream(file)));
395
396                parseClassification(buffer);
397
398        }
399
400        private void parseHierarchy() throws IOException{
401
402                File file = new File(getHieFilename());
403
404
405                InputStreamProvider ips = new InputStreamProvider();
406                BufferedReader buffer = new BufferedReader (new InputStreamReader(ips.getInputStream(file)));
407
408                parseHierarchy(buffer);
409
410        }
411
412        private void parseHierarchy(BufferedReader buffer) throws IOException {
413                String line;
414
415                int counter =0;
416                while ((line = buffer.readLine ()) != null) {
417                        if ( line.startsWith("#"))
418                                continue;
419
420                        String[] spl  = line.split("\t");
421
422                        if ( spl.length != 3 ) {
423                                throw new IOException("parseHierarchy: Can't parse line " + line +" (length: " + spl.length+")");
424                        }
425                        counter++;
426                        int sunid       = Integer.parseInt(spl[0]);
427                        int parentSunid = -1;
428
429                        if ( sunid != 0)
430                                parentSunid = Integer.parseInt(spl[1]);
431
432                        String children = spl[2];
433                        String[] childIds = children.split(",");
434
435                        List<Integer> chis = new ArrayList<Integer>();
436
437                        for ( String id : childIds){
438                                if ( id.equals("-"))
439                                        continue;
440                                chis.add(Integer.parseInt(id));
441                        }
442
443                        ScopNode node = new ScopNode();
444
445                        node.setSunid(sunid);
446                        node.setParentSunid(parentSunid);
447                        node.setChildren(chis);
448
449                        scopTree.put(sunid, node);
450                }
451                logger.info("Parsed {} SCOP sunid nodes.", counter);
452        }
453
454
455        private void parseDescriptions() throws IOException{
456
457                File file = new File(getDesFilename());
458
459                InputStreamProvider ips = new InputStreamProvider();
460                BufferedReader buffer = new BufferedReader (new InputStreamReader(ips.getInputStream(file)));
461
462                parseDescriptions(buffer);
463
464        }
465
466        private void parseComments() throws IOException{
467
468                File file = new File(getComFilename());
469
470                InputStreamProvider ips = new InputStreamProvider();
471                BufferedReader buffer = new BufferedReader (new InputStreamReader(ips.getInputStream(file)));
472
473                parseComments(buffer);
474
475        }
476
477        private void parseComments(BufferedReader buffer) throws IOException {
478
479                commentsMap = new HashMap<Integer,List<String>>();
480
481                int counter = 0;
482                String line;
483                while ((line = buffer.readLine ()) != null) {
484                        if (line.startsWith("#")) continue;
485                        String[] parts = line.split("!");
486                        int sunId = Integer.parseInt(parts[0].trim());
487                        if (parts.length == 1) {
488                                commentsMap.put(sunId, new ArrayList<String>(1));
489                                continue;
490                        }
491                        List<String> comments = new ArrayList<String>(parts.length - 1);
492                        for (int i = 1; i < parts.length; i++) {
493                                String trimmed = parts[i].trim();
494                                if( !trimmed.isEmpty() ) {
495                                        comments.add(trimmed);
496                                }
497                        }
498                        commentsMap.put(sunId, comments);
499                        counter++;
500                }
501                logger.info("Parsed {} SCOP comments.", counter);
502
503        }
504
505        private void parseDescriptions(BufferedReader buffer) throws IOException {
506                String line = null;
507
508                int counter = 0;
509                while ((line = buffer.readLine ()) != null) {
510                        if ( line.startsWith("#"))
511                                continue;
512
513                        String[] spl  = line.split("\t");
514
515                        if ( spl.length != 5 ) {
516                                throw new IOException("parseDescriptions: Can't parse line " + line +" (length: " + spl.length+")");
517                        }
518                        counter++;
519
520                        //46464  dm  a.1.1.2 -   Hemoglobin I
521                        int sunID = Integer.parseInt(spl[0]);
522                        ScopCategory category =  ScopCategory.fromString(spl[1]);
523                        String classificationId = spl[2];
524                        String name = spl[3];
525                        String desc = spl[4];
526
527                        ScopDescription c = new ScopDescription();
528                        c.setSunID(sunID);
529                        c.setCategory(category);
530                        c.setClassificationId(classificationId);
531                        c.setName(name);
532                        c.setDescription(desc);
533
534                        sunidMap.put(sunID, c);
535
536                }
537                logger.info("Parsed {} SCOP sunid descriptions.", counter);
538        }
539
540
541
542        private void parseClassification(BufferedReader buffer) throws IOException {
543                String line = null;
544
545                int counter = 0;
546                while ((line = buffer.readLine ()) != null) {
547                        if ( line.startsWith("#"))
548                                continue;
549
550                        String[] spl  = line.split("\t");
551
552                        if ( spl.length != 6){
553                                throw new IOException("Can't parse line " + line);
554                        }
555                        counter++;
556
557                        String scopId = spl[0];
558                        String pdbId = spl[1];
559                        String range = spl[2];
560                        String classificationId = spl[3];
561                        Integer sunid = Integer.parseInt(spl[4]);
562                        String tree = spl[5];
563
564
565
566                        ScopDomain d = new ScopDomain();
567                        d.setScopId(scopId);
568                        d.setPdbId(pdbId);
569
570                        d.setRanges(extractRanges(range));
571
572                        d.setClassificationId(classificationId);
573                        d.setSunid(sunid);
574
575                        String[] treeSplit = tree.split(",");
576
577                        if (  treeSplit.length != 7 ) {
578                                throw new IOException("Can't process: " + line );
579                        }
580
581                        int classId =Integer.parseInt(treeSplit[0].substring(3));
582                        int foldId = Integer.parseInt(treeSplit[1].substring(3));
583                        int superfamilyId = Integer.parseInt(treeSplit[2].substring(3));
584                        int familyId = Integer.parseInt(treeSplit[3].substring(3));
585                        int domainId = Integer.parseInt(treeSplit[4].substring(3));
586                        int speciesId = Integer.parseInt(treeSplit[5].substring(3));
587                        int px = Integer.parseInt(treeSplit[6].substring(3));
588
589                        d.setClassId(classId);
590                        d.setFoldId(foldId);
591                        d.setSuperfamilyId(superfamilyId);
592                        d.setFamilyId(familyId);
593                        d.setDomainId(domainId);
594                        d.setSpeciesId(speciesId);
595                        d.setPx(px);
596
597                        List<ScopDomain> domainList;
598                        if ( domainMap.containsKey(pdbId)){
599                                domainList = domainMap.get(pdbId);
600                        } else {
601                                domainList = new ArrayList<ScopDomain>();
602                                domainMap.put(pdbId,domainList);
603                        }
604
605                        domainList.add(d);
606                }
607                logger.info("Parsed {} SCOP sunid domains.", counter);
608
609        }
610
611        /**
612         * Converts the SCOP range field into a list of subranges suitable for
613         * storage in a ScopDomain object. Each range should be of a format
614         * compatible with {@link StructureTools#getSubRanges(Structure,String)}.
615         * @param range
616         * @return
617         */
618        private List<String> extractRanges(String range) {
619                List<String> ranges;
620                String[] rangeSpl = range.split(",");
621
622                // Recent versions of scop always specify a chain, so no processing is needed
623                if(scopVersion.compareTo("1.73") < 0 ) {
624                        for(int i=0; i<rangeSpl.length;i++) {
625                                String subRange = rangeSpl[i];
626
627                                // Allow single-chains, as well as the '-' special case
628                                if(subRange.length()<2) {
629                                        continue;
630                                }
631
632                                // Allow explicit chain syntax
633                                if(subRange.charAt(1) != ':') {
634                                        // Early versions sometimes skip the chain identifier for single-chain domains
635                                        // Indicate this with a chain "_"
636                                        rangeSpl[i] = "_:"+subRange;
637                                }
638                        }
639                }
640                ranges = Arrays.asList(rangeSpl);
641                return ranges;
642        }
643
644        protected void downloadClaFile() throws FileNotFoundException, IOException{
645                if(mirrors.size()<1) {
646                        initScopURLs();
647                }
648                IOException exception = null;
649                for(ScopMirror mirror:mirrors) {
650                        try {
651                                URL url = new URL(mirror.getClaURL(scopVersion));
652
653                                String localFileName = getClaFilename();
654                                File localFile = new File(localFileName);
655
656                                downloadFileFromRemote(url, localFile);
657                                return;
658                        } catch(IOException e ) {
659                                exception = e;
660                        }
661                }
662                throw new IOException("Unable to download SCOP .cla file",exception);
663        }
664
665        protected void downloadDesFile() throws FileNotFoundException, IOException{
666                if(mirrors.size()<1) {
667                        initScopURLs();
668                }
669                IOException exception = null;
670                for(ScopMirror mirror:mirrors) {
671                        try {
672                                URL url = new URL(mirror.getDesURL( scopVersion));
673
674                                String localFileName = getDesFilename();
675                                File localFile = new File(localFileName);
676
677                                downloadFileFromRemote(url, localFile);
678                                return;
679                        } catch(IOException e ) {
680                                exception = e;
681                        }
682                }
683                throw new IOException("Unable to download SCOP .des file",exception);
684        }
685
686        protected void downloadHieFile() throws IOException{
687                if(mirrors.size()<1) {
688                        initScopURLs();
689                }
690                IOException exception = null;
691                for(ScopMirror mirror:mirrors) {
692                        try {
693                                URL url = new URL(mirror.getHieURL( scopVersion));
694
695                                String localFileName = getHieFilename();
696                                File localFile = new File(localFileName);
697
698                                downloadFileFromRemote(url, localFile);
699                                return;
700                        } catch(IOException e ) {
701                                exception = e;
702                        }
703                }
704                throw new IOException("Unable to download SCOP .hie file",exception);
705
706        }
707
708        protected void downloadComFile() throws FileNotFoundException, IOException{
709                if(mirrors.size()<1) {
710                        initScopURLs();
711                }
712                IOException exception = null;
713                for(ScopMirror mirror:mirrors) {
714                        try {
715                                URL url = new URL(mirror.getComURL(scopVersion));
716
717                                String localFileName = getComFilename();
718                                File localFile = new File(localFileName);
719
720                                downloadFileFromRemote(url, localFile);
721                                return;
722                        } catch (IOException e ) {
723                                exception = e;
724                        }
725                }
726                throw new IOException("Unable to download SCOP .com file",exception);
727        }
728
729        protected void downloadFileFromRemote(URL remoteURL, File localFile) throws IOException{
730                logger.info("Downloading " + remoteURL + " to: " + localFile);
731                FileDownloadUtils.downloadFile(remoteURL, localFile);
732        }
733
734        private boolean claFileAvailable(){
735                String fileName = getClaFilename();
736
737                File f = new File(fileName);
738
739                return f.exists() && f.length()>0;
740        }
741
742        private boolean desFileAvailable(){
743                String fileName = getDesFilename();
744
745                File f = new File(fileName);
746                return f.exists() && f.length()>0;
747        }
748
749        private boolean hieFileAvailable(){
750                String fileName = getHieFilename();
751
752                File f = new File(fileName);
753
754                return f.exists() && f.length()>0;
755        }
756
757        private boolean comFileAvailable(){
758                String fileName = getComFilename();
759
760                File f = new File(fileName);
761
762                return f.exists() && f.length()>0;
763        }
764
765        protected String getClaFilename(){
766                return cacheLocation + claFileName + scopVersion;
767        }
768
769        protected String getDesFilename(){
770                return cacheLocation + desFileName + scopVersion;
771
772        }
773
774        protected String getHieFilename(){
775                return cacheLocation + hieFileName + scopVersion;
776
777        }
778
779        protected String getComFilename(){
780                return cacheLocation + comFileName + scopVersion;
781        }
782
783        public String getCacheLocation() {
784                return cacheLocation;
785        }
786
787        public void setCacheLocation(String cacheLocation) {
788
789                if (! cacheLocation.endsWith(FILESPLIT))
790                        cacheLocation += FILESPLIT;
791                this.cacheLocation = cacheLocation;
792
793
794        }
795        /* (non-Javadoc)
796         * @see org.biojava.nbio.structure.scop.ScopDatabase#getScopVersion()
797         */
798        @Override
799        public String getScopVersion() {
800                return scopVersion;
801        }
802        @Override
803        public void setScopVersion(String scopVersion) {
804                if(scopVersion == null)
805                        throw new NullPointerException("Null scop version");
806                if(this.scopVersion.equals(scopVersion))
807                        return;
808                this.scopVersion = scopVersion;
809                // reset installation flags
810                installedCla.set(false);
811                installedDes.set(false);
812                installedHie.set(false);
813                installedCom.set(false);
814
815        }
816
817        public void addMirror(String scopDownloadURL) {
818                mirrors.add(new ScopMirror(scopDownloadURL));
819        }
820        void addMirror(ScopMirror scopURLs) {
821                mirrors.add(scopURLs);
822        }
823        public List<ScopMirror> getMirrors() {
824                if(mirrors.isEmpty()) {
825                        this.initScopURLs();
826                }
827                return mirrors;
828        }
829
830        /* (non-Javadoc)
831         * @see org.biojava.nbio.structure.scop.ScopDatabase#getScopDomainsBySunid(java.lang.Integer)
832         */
833        @Override
834        public List<ScopDomain> getScopDomainsBySunid(Integer sunid)
835        {
836
837                try {
838                        ensureClaInstalled();
839                } catch (IOException e) {
840                        throw new ScopIOException(e);
841                }
842
843                List<ScopDomain> domains = new ArrayList<ScopDomain>();
844
845                for (String pdbId: domainMap.keySet()){
846                        for (ScopDomain d : domainMap.get(pdbId)){
847                                try {
848                                        if ( d.getPx() == sunid) {
849                                                domains.add((ScopDomain)d.clone());
850                                        } else if ( d.getSpeciesId() == sunid ){
851                                                domains.add((ScopDomain)d.clone());
852                                        }else if ( d.getDomainId() == sunid ){
853                                                domains.add((ScopDomain)d.clone());
854                                        }else if ( d.getFamilyId() == sunid ){
855                                                domains.add((ScopDomain)d.clone());
856                                        }else if ( d.getSuperfamilyId() == sunid ){
857                                                domains.add((ScopDomain)d.clone());
858                                        }else if ( d.getFoldId() == sunid ){
859                                                domains.add((ScopDomain)d.clone());
860                                        }else if ( d.getClassId() == sunid ){
861                                                domains.add((ScopDomain)d.clone());
862                                        } else {
863                                                throw new RuntimeException("Type " + d + " not recognized"); // only possible if SCOP changes
864                                        }
865                                } catch (CloneNotSupportedException e){
866                                        throw new RuntimeException(ScopDomain.class + " subclass does not support clone()", e);
867                                }
868                        }
869                }
870                return domains;
871
872        }
873
874        @Override
875        public List<String> getComments(int sunid) {
876                try {
877                        ensureComInstalled();
878                } catch (IOException e) {
879                        throw new ScopIOException(e);
880                }
881                if (!commentsMap.containsKey(sunid)) return new ArrayList<String>(1);
882                return commentsMap.get(sunid);
883        }
884
885
886        private void initScopURLs() {
887                if(!this.mirrors.isEmpty()) {
888                        return;
889                }
890
891                // first, try default scop
892                ScopMirror primary = new ScopMirror();
893                // If unreachable, try alternate Berkeley location
894                ScopMirror alt = new ScopMirror(
895                                SCOP_DOWNLOAD_ALTERNATE,
896                                "dir.cla.scop.%s.txt","dir.des.scop.%s.txt",
897                                "dir.hie.scop.%s.txt","dir.com.scop.%s.txt");
898                mirrors.add(primary);
899                mirrors.add(alt);
900        }
901}