Clover icon

Coverage Report

  1. Project Clover database Thu Dec 4 2025 16:11:35 GMT
  2. Package jalview.ws.jws2

File SeqAnnotationServiceCalcWorker.java

 

Coverage histogram

../../../img/srcFileCovDistChart0.png
60% of files have more coverage

Code metrics

126
239
25
1
835
620
119
0.5
9.56
25
4.76

Classes

Class Line # Actions
SeqAnnotationServiceCalcWorker 68 239 119
0.00%
 

Contributing tests

No tests hitting this source file were found.

Source view

1    /*
2    * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3    * Copyright (C) $$Year-Rel$$ The Jalview Authors
4    *
5    * This file is part of Jalview.
6    *
7    * Jalview is free software: you can redistribute it and/or
8    * modify it under the terms of the GNU General Public License
9    * as published by the Free Software Foundation, either version 3
10    * of the License, or (at your option) any later version.
11    *
12    * Jalview is distributed in the hope that it will be useful, but
13    * WITHOUT ANY WARRANTY; without even the implied warranty
14    * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15    * PURPOSE. See the GNU General Public License for more details.
16    *
17    * You should have received a copy of the GNU General Public License
18    * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19    * The Jalview Authors are detailed in the 'AUTHORS' file.
20    */
21    package jalview.ws.jws2;
22   
23    import jalview.analysis.AlignSeq;
24    import jalview.analysis.AlignmentAnnotationUtils;
25    import jalview.analysis.SeqsetUtils;
26    import jalview.api.AlignViewportI;
27    import jalview.api.AlignmentViewPanel;
28    import jalview.api.FeatureColourI;
29    import jalview.api.PollableAlignCalcWorkerI;
30    import jalview.bin.Cache;
31    import jalview.bin.Console;
32    import jalview.datamodel.AlignmentAnnotation;
33    import jalview.datamodel.AlignmentI;
34    import jalview.datamodel.AnnotatedCollectionI;
35    import jalview.datamodel.Annotation;
36    import jalview.datamodel.ContiguousI;
37    import jalview.datamodel.Mapping;
38    import jalview.datamodel.SequenceI;
39    import jalview.datamodel.features.FeatureMatcherSetI;
40    import jalview.gui.AlignFrame;
41    import jalview.gui.Desktop;
42    import jalview.gui.IProgressIndicator;
43    import jalview.gui.IProgressIndicatorHandler;
44    import jalview.gui.JvOptionPane;
45    import jalview.gui.WebserviceInfo;
46    import jalview.schemes.FeatureSettingsAdapter;
47    import jalview.schemes.ResidueProperties;
48    import jalview.util.MapList;
49    import jalview.util.MessageManager;
50    import jalview.workers.AlignCalcWorker;
51    import jalview.ws.JobStateSummary;
52    import jalview.ws.api.CancellableI;
53    import jalview.ws.api.JalviewServiceEndpointProviderI;
54    import jalview.ws.api.JobId;
55    import jalview.ws.api.SequenceAnnotationServiceI;
56    import jalview.ws.api.ServiceWithParameters;
57    import jalview.ws.api.WSAnnotationCalcManagerI;
58    import jalview.ws.gui.AnnotationWsJob;
59    import jalview.ws.jws2.dm.AAConSettings;
60    import jalview.ws.params.ArgumentI;
61    import jalview.ws.params.WsParamSetI;
62   
63    import java.util.ArrayList;
64    import java.util.HashMap;
65    import java.util.List;
66    import java.util.Map;
67   
 
68    public class SeqAnnotationServiceCalcWorker extends AlignCalcWorker
69    implements WSAnnotationCalcManagerI, PollableAlignCalcWorkerI
70    {
71   
72    protected ServiceWithParameters service;
73   
74    protected WsParamSetI preset;
75   
76    protected List<ArgumentI> arguments;
77   
78    protected IProgressIndicator guiProgress;
79   
80    protected boolean submitGaps = true;
81   
82    /**
83    * by default, we filter out non-standard residues before submission
84    */
85    protected boolean filterNonStandardResidues = true;
86   
87    /**
88    * Recover any existing parameters for this service
89    */
 
90  0 toggle protected void initViewportParams()
91    {
92  0 if (getCalcId() != null)
93    {
94  0 ((jalview.gui.AlignViewport) alignViewport).setCalcIdSettingsFor(
95    getCalcId(),
96    new AAConSettings(true, service, this.preset, arguments),
97    true);
98    }
99    }
100   
101    /**
102    *
103    * @return null or a string used to recover all annotation generated by this
104    * worker
105    */
 
106  0 toggle public String getCalcId()
107    {
108  0 return service.getAlignAnalysisUI() == null ? null
109    : service.getAlignAnalysisUI().getCalcId();
110    }
111   
 
112  0 toggle public WsParamSetI getPreset()
113    {
114  0 return preset;
115    }
116   
 
117  0 toggle public List<ArgumentI> getArguments()
118    {
119  0 return arguments;
120    }
121   
122    /**
123    * reconfigure and restart the AAConClient. This method will spawn a new
124    * thread that will wait until any current jobs are finished, modify the
125    * parameters and restart the conservation calculation with the new values.
126    *
127    * @param newpreset
128    * @param newarguments
129    */
 
130  0 toggle public void updateParameters(final WsParamSetI newpreset,
131    final List<ArgumentI> newarguments)
132    {
133  0 preset = newpreset;
134  0 arguments = newarguments;
135  0 calcMan.startWorker(this);
136  0 initViewportParams();
137    }
138    protected boolean alignedSeqs = true;
139   
140    protected boolean nucleotidesAllowed = false;
141   
142    protected boolean proteinAllowed = false;
143   
144    /**
145    * record sequences for mapping result back to afterwards
146    */
147    protected boolean bySequence = false;
148   
149    protected Map<String, SequenceI> seqNames;
150   
151    // TODO: convert to bitset
152    protected boolean[] gapMap;
153   
154    int realw;
155   
156    protected int start;
157   
158    int end;
159   
160    private AlignFrame alignFrame;
161   
 
162  0 toggle public boolean[] getGapMap()
163    {
164  0 return gapMap;
165    }
166   
 
167  0 toggle public SeqAnnotationServiceCalcWorker(ServiceWithParameters service,
168    AlignFrame alignFrame,
169    WsParamSetI preset, List<ArgumentI> paramset)
170    {
171  0 super(alignFrame.getCurrentView(), alignFrame.alignPanel);
172    // TODO: both these fields needed ?
173  0 this.alignFrame = alignFrame;
174  0 this.guiProgress = alignFrame;
175  0 this.preset = preset;
176  0 this.arguments = paramset;
177  0 this.service = service;
178  0 try
179    {
180  0 annotService = (jalview.ws.api.SequenceAnnotationServiceI) ((JalviewServiceEndpointProviderI) service)
181    .getEndpoint();
182    } catch (ClassCastException cce)
183    {
184  0 annotService = null;
185  0 JvOptionPane.showMessageDialog(Desktop.getInstance(),
186    MessageManager.formatMessage(
187    "label.service_called_is_not_an_annotation_service",
188    new String[]
189    { service.getName() }),
190    MessageManager.getString("label.internal_jalview_error"),
191    JvOptionPane.WARNING_MESSAGE);
192   
193    }
194  0 cancellable = CancellableI.class.isInstance(annotService);
195    // configure submission flags
196  0 proteinAllowed = service.isProteinService();
197  0 nucleotidesAllowed = service.isNucleotideService();
198  0 alignedSeqs = service.isNeedsAlignedSequences();
199  0 bySequence = !service.isAlignmentAnalysis();
200  0 filterNonStandardResidues = service.isFilterSymbols();
201  0 min_valid_seqs = service.getMinimumInputSequences();
202  0 submitGaps = service.isAlignmentAnalysis();
203   
204  0 if (service.isInteractiveUpdate())
205    {
206  0 initViewportParams();
207    }
208    }
209   
210    /**
211    *
212    * @return true if the submission thread should attempt to submit data
213    */
 
214  0 toggle public boolean hasService()
215    {
216  0 return annotService != null;
217    }
218   
219    protected SequenceAnnotationServiceI annotService;
220    protected final boolean cancellable;
221   
222    volatile JobId rslt = null;
223   
224    AnnotationWsJob running = null;
225   
226    private int min_valid_seqs;
227   
228   
229    private long progressId = -1;
230    JobStateSummary job = null;
231    WebserviceInfo info = null;
232    List<SequenceI> seqs = null;
233   
 
234  0 toggle @Override public void startUp() throws Throwable
235    {
236  0 if (alignViewport.isClosed())
237    {
238  0 abortAndDestroy();
239  0 return;
240    }
241  0 if (!hasService())
242    {
243  0 return;
244    }
245   
246  0 StringBuffer msg = new StringBuffer();
247  0 job = new JobStateSummary();
248  0 info = new WebserviceInfo("foo", "bar", false);
249   
250  0 seqs = getInputSequences(
251    alignViewport.getAlignment(),
252  0 bySequence ? alignViewport.getSelectionGroup() : null);
253   
254  0 if (seqs == null || !checkValidInputSeqs(seqs))
255    {
256  0 jalview.bin.Console.debug(
257    "Sequences for analysis service were null or not valid");
258  0 return;
259    }
260   
261  0 if (guiProgress != null)
262    {
263  0 guiProgress.setProgressBar(service.getActionText(),
264    progressId = System.currentTimeMillis());
265    }
266  0 jalview.bin.Console.debug("submitted " + seqs.size()
267    + " sequences to " + service.getActionText());
268   
269  0 rslt = annotService.submitToService(seqs, getPreset(),
270    getArguments());
271  0 if (rslt == null)
272    {
273  0 return;
274    }
275    // TODO: handle job submission error reporting here.
276  0 Console.debug("Service " + service.getUri() + "\nSubmitted job ID: "
277    + rslt);
278    // ///
279    // otherwise, construct WsJob and any UI handlers
280  0 running = new AnnotationWsJob();
281  0 running.setJobHandle(rslt);
282  0 running.setSeqNames(seqNames);
283  0 running.setStartPos(start);
284  0 running.setSeqs(seqs);
285  0 job.updateJobPanelState(info, "", running);
286  0 if (guiProgress != null)
287    {
288  0 guiProgress.registerHandler(progressId,
289    new IProgressIndicatorHandler()
290    {
291   
 
292  0 toggle @Override
293    public boolean cancelActivity(long id)
294    {
295  0 calcMan.cancelWorker(SeqAnnotationServiceCalcWorker.this);
296  0 return true;
297    }
298   
 
299  0 toggle @Override
300    public boolean canCancel()
301    {
302  0 return cancellable;
303    }
304    });
305    }
306    }
307   
 
308  0 toggle @Override public boolean poll() throws Throwable
309    {
310  0 boolean finished = false;
311   
312  0 Console.debug("Updating status for annotation service.");
313  0 annotService.updateStatus(running);
314  0 job.updateJobPanelState(info, "", running);
315  0 if (running.isSubjobComplete())
316    {
317  0 Console.debug(
318    "Finished polling analysis service job: status reported is "
319    + running.getState());
320  0 finished = true;
321    }
322    else
323    {
324  0 Console.debug("Status now " + running.getState());
325    }
326   
327    // pull any stats - some services need to flush log output before
328    // results are available
329  0 Console.debug("Updating progress log for annotation service.");
330   
331  0 try
332    {
333  0 annotService.updateJobProgress(running);
334    } catch (Throwable thr)
335    {
336  0 Console.debug("Ignoring exception during progress update.",
337    thr);
338    }
339  0 Console.debug("Result of poll: " + running.getStatus());
340   
341   
342  0 if (finished)
343    {
344  0 Console.debug("Job poll loop exited. Job is " + running.getState());
345  0 if (running.isFinished())
346    {
347    // expect there to be results to collect
348    // configure job with the associated view's feature renderer, if one
349    // exists.
350    // TODO: here one would also grab the 'master feature renderer' in order
351    // to enable/disable
352    // features automatically according to user preferences
353  0 running.setFeatureRenderer(
354    ((jalview.gui.AlignmentPanel) ap).cloneFeatureRenderer());
355  0 Console.debug("retrieving job results.");
356  0 final Map<String, FeatureColourI> featureColours = new HashMap<>();
357  0 final Map<String, FeatureMatcherSetI> featureFilters = new HashMap<>();
358  0 List<AlignmentAnnotation> returnedAnnot = annotService
359    .getAnnotationResult(running.getJobHandle(), seqs,
360    featureColours, featureFilters);
361   
362  0 Console.debug("Obtained " + (returnedAnnot == null ? "no rows"
363    : ("" + returnedAnnot.size())));
364  0 Console.debug("There were " + featureColours.size()
365    + " feature colours and " + featureFilters.size()
366    + " filters defined.");
367   
368    // TODO
369    // copy over each annotation row reurned and also defined on each
370    // sequence, excluding regions not annotated due to gapMap/column
371    // visibility
372   
373    // update calcId if it is not already set on returned annotation
374  0 if (returnedAnnot != null)
375    {
376  0 for (AlignmentAnnotation aa : returnedAnnot)
377    {
378    // assume that any CalcIds already set
379  0 if (getCalcId() != null && aa.getCalcId() == null
380    || "".equals(aa.getCalcId()))
381    {
382  0 aa.setCalcId(getCalcId());
383    }
384    // autocalculated annotation are created by interactive alignment
385    // analysis services
386  0 aa.autoCalculated = service.isAlignmentAnalysis()
387    && service.isInteractiveUpdate();
388    }
389    }
390   
391  0 running.setAnnotation(returnedAnnot);
392   
393  0 if (running.hasResults())
394    {
395  0 jalview.bin.Console.debug("Updating result annotation from Job "
396    + rslt + " at " + service.getUri());
397  0 updateResultAnnotation(true);
398  0 if (running.isTransferSequenceFeatures())
399    {
400    // TODO
401    // look at each sequence and lift over any features, excluding
402    // regions
403    // not annotated due to gapMap/column visibility
404   
405  0 jalview.bin.Console.debug(
406    "Updating feature display settings and transferring features from Job "
407    + rslt + " at " + service.getUri());
408    // TODO: consider merge rather than apply here
409  0 alignViewport.applyFeaturesStyle(new FeatureSettingsAdapter()
410    {
 
411  0 toggle @Override
412    public FeatureColourI getFeatureColour(String type)
413    {
414  0 return featureColours.get(type);
415    }
416   
 
417  0 toggle @Override
418    public FeatureMatcherSetI getFeatureFilters(String type)
419    {
420  0 return featureFilters.get(type);
421    }
422   
 
423  0 toggle @Override
424    public boolean isFeatureDisplayed(String type)
425    {
426  0 return featureColours.containsKey(type);
427    }
428   
429    });
430    // TODO: JAL-1150 - create sequence feature settings API for
431    // defining
432    // styles and enabling/disabling feature overlay on alignment panel
433   
434  0 if (alignFrame.alignPanel == ap)
435    {
436  0 alignViewport.setShowSequenceFeatures(true);
437  0 alignFrame.setMenusForViewport();
438    }
439    }
440  0 ap.adjustAnnotationHeight();
441    }
442    }
443  0 Console.debug("Annotation Service Worker thread finished.");
444   
445    }
446   
447  0 return finished;
448    }
449   
 
450  0 toggle @Override public void cancel()
451    {
452  0 cancelCurrentJob();
453    }
454   
 
455  0 toggle @Override public void done()
456    {
457  0 if (ap != null)
458    {
459  0 if (guiProgress != null && progressId != -1)
460    {
461  0 guiProgress.removeProgressBar(progressId);
462    }
463    // TODO: may not need to paintAlignment again !
464  0 ap.paintAlignment(false, false);
465    }
466    }
467   
468    /**
469    * validate input for dynamic/non-dynamic update context TODO: move to
470    * analysis interface ?
471    * @param seqs
472    *
473    * @return true if input is valid
474    */
 
475  0 toggle boolean checkValidInputSeqs(List<SequenceI> seqs)
476    {
477  0 int nvalid = 0;
478  0 for (SequenceI sq : seqs)
479    {
480  0 if (sq.getStart() <= sq.getEnd()
481  0 && (sq.isProtein() ? proteinAllowed : nucleotidesAllowed))
482    {
483  0 if (submitGaps
484    || sq.getLength() == (sq.getEnd() - sq.getStart() + 1))
485    {
486  0 nvalid++;
487    }
488    }
489    }
490  0 return nvalid >= min_valid_seqs;
491    }
492   
 
493  0 toggle public void cancelCurrentJob()
494    {
495  0 try
496    {
497  0 String id = running.getJobId();
498  0 if (cancellable && ((CancellableI) annotService).cancel(running))
499    {
500  0 System.err.println("Cancelled job " + id);
501    }
502    else
503    {
504  0 System.err.println("Job " + id + " couldn't be cancelled.");
505    }
506    } catch (Exception q)
507    {
508  0 q.printStackTrace();
509    }
510    }
511   
512    /**
513    * Interactive updating. Analysis calculations that work on the currently
514    * displayed alignment data should cancel existing jobs when the input data
515    * has changed.
516    *
517    * @return true if a running job should be cancelled because new input data is
518    * available for analysis
519    */
 
520  0 toggle boolean isInteractiveUpdate()
521    {
522  0 return service.isInteractiveUpdate();
523    }
524   
525    /**
526    * decide what sequences will be analysed TODO: refactor to generate
527    * List<SequenceI> for submission to service interface
528    *
529    * @param alignment
530    * @param inputSeqs
531    * @return
532    */
 
533  0 toggle public List<SequenceI> getInputSequences(AlignmentI alignment,
534    AnnotatedCollectionI inputSeqs)
535    {
536  0 if (alignment == null || alignment.getWidth() <= 0
537    || alignment.getSequences() == null || alignment.isNucleotide()
538    ? !nucleotidesAllowed
539    : !proteinAllowed)
540    {
541  0 return null;
542    }
543  0 if (inputSeqs == null || inputSeqs.getWidth() <= 0
544    || inputSeqs.getSequences() == null
545    || inputSeqs.getSequences().size() < 1)
546    {
547  0 inputSeqs = alignment;
548    }
549   
550  0 List<SequenceI> seqs = new ArrayList<>();
551   
552  0 int minlen = 10;
553  0 int ln = -1;
554  0 if (bySequence)
555    {
556  0 seqNames = new HashMap<>();
557    }
558  0 gapMap = new boolean[0];
559  0 start = inputSeqs.getStartRes();
560  0 end = inputSeqs.getEndRes();
561    // TODO: URGENT! unify with JPred / MSA code to handle hidden regions
562    // correctly
563    // TODO: push attributes into WsJob instance (so they can be safely
564    // persisted/restored
565  0 for (SequenceI sq : (inputSeqs.getSequences()))
566    {
567  0 if (bySequence
568    ? sq.findPosition(end + 1)
569    - sq.findPosition(start + 1) > minlen - 1
570    : sq.getEnd() - sq.getStart() > minlen - 1)
571    {
572  0 String newname = SeqsetUtils.unique_name(seqs.size() + 1);
573    // make new input sequence with or without gaps
574  0 if (seqNames != null)
575    {
576  0 seqNames.put(newname, sq);
577    }
578  0 SequenceI seq;
579  0 if (submitGaps)
580    {
581  0 seqs.add(seq = new jalview.datamodel.Sequence(newname,
582    sq.getSequenceAsString()));
583  0 if (gapMap == null || gapMap.length < seq.getLength())
584    {
585  0 boolean[] tg = gapMap;
586  0 gapMap = new boolean[seq.getLength()];
587  0 System.arraycopy(tg, 0, gapMap, 0, tg.length);
588  0 for (int p = tg.length; p < gapMap.length; p++)
589    {
590  0 gapMap[p] = false; // init as a gap
591    }
592    }
593  0 for (int apos : sq.gapMap())
594    {
595  0 char sqc = sq.getCharAt(apos);
596  0 if (!filterNonStandardResidues
597  0 || (sq.isProtein() ? ResidueProperties.aaIndex[sqc] < 20
598    : ResidueProperties.nucleotideIndex[sqc] < 5))
599    {
600  0 gapMap[apos] = true; // aligned and real amino acid residue
601    }
602  0 ;
603    }
604    }
605    else
606    {
607    // TODO: add ability to exclude hidden regions
608  0 seqs.add(seq = new jalview.datamodel.Sequence(newname,
609    AlignSeq.extractGaps(jalview.util.Comparison.GapChars,
610    sq.getSequenceAsString(start, end + 1))));
611    // for annotation need to also record map to sequence start/end
612    // position in range
613    // then transfer back to original sequence on return.
614    }
615  0 if (seq.getLength() > ln)
616    {
617  0 ln = seq.getLength();
618    }
619    }
620    }
621  0 if (alignedSeqs && submitGaps)
622    {
623  0 realw = 0;
624  0 for (int i = 0; i < gapMap.length; i++)
625    {
626  0 if (gapMap[i])
627    {
628  0 realw++;
629    }
630    }
631    // try real hard to return something submittable
632    // TODO: some of AAcon measures need a minimum of two or three amino
633    // acids at each position, and AAcon doesn't gracefully degrade.
634  0 for (int p = 0; p < seqs.size(); p++)
635    {
636  0 SequenceI sq = seqs.get(p);
637    // strip gapped columns
638  0 char[] padded = new char[realw],
639    orig = sq.getSequence();
640  0 for (int i = 0, pp = 0; i < realw; pp++)
641    {
642  0 if (gapMap[pp])
643    {
644  0 if (orig.length > pp)
645    {
646  0 padded[i++] = orig[pp];
647    }
648    else
649    {
650  0 padded[i++] = '-';
651    }
652    }
653    }
654  0 seqs.set(p, new jalview.datamodel.Sequence(sq.getName(),
655    new String(padded)));
656    }
657    }
658  0 return seqs;
659    }
660   
 
661  0 toggle @Override
662    public void updateAnnotation()
663    {
664  0 updateResultAnnotation(false);
665    }
666   
 
667  0 toggle public void updateResultAnnotation(boolean immediate)
668    {
669  0 if ((immediate || !calcMan.isWorking(this)) && running != null
670    && running.hasResults())
671    {
672  0 List<AlignmentAnnotation> ourAnnot = running.getAnnotation(),
673    newAnnots = new ArrayList<>();
674    //
675    // update graphGroup for all annotation
676    //
677    /**
678    * find a graphGroup greater than any existing ones this could be a method
679    * provided by alignment Alignment.getNewGraphGroup() - returns next
680    * unused graph group
681    */
682  0 int graphGroup = 1;
683  0 if (alignViewport.getAlignment().getAlignmentAnnotation() != null)
684    {
685  0 for (AlignmentAnnotation ala : alignViewport.getAlignment()
686    .getAlignmentAnnotation())
687    {
688  0 if (ala.graphGroup > graphGroup)
689    {
690  0 graphGroup = ala.graphGroup;
691    }
692    }
693    }
694    /**
695    * update graphGroup in the annotation rows returned from service
696    */
697    // TODO: look at sequence annotation rows and update graph groups in the
698    // case of reference annotation.
699  0 for (AlignmentAnnotation ala : ourAnnot)
700    {
701  0 if (ala.graphGroup > 0)
702    {
703  0 ala.graphGroup += graphGroup;
704    }
705  0 SequenceI aseq = (ala.sequenceRef == null) ? null
706    : running.getSeqNames().get(ala.sequenceRef.getName());
707    /*
708    * transfer sequence refs and adjust gapmap
709    * Prepare an array for annotations and copy returned annotations to it
710    * according to gapMap if present.
711    */
712  0 Annotation[] resAnnot = ala.annotations,
713    gappedAnnot = new Annotation[Math.max(
714    alignViewport.getAlignment().getWidth(),
715    gapMap.length)];
716  0 for (int p = 0, ap = 0; ap < gappedAnnot.length; ap++)
717    {
718  0 if (gapMap != null && gapMap.length > ap && !gapMap[ap])
719    {
720  0 gappedAnnot[ap] = new Annotation("", "", ' ', Float.NaN);
721    }
722  0 else if (p < resAnnot.length)
723    {
724  0 gappedAnnot[ap] = resAnnot[p++];
725    }
726    }
727  0 ala.sequenceRef = aseq;
728  0 AlignmentAnnotation newAnnot = getAlignViewport().getAlignment()
729    .updateFromOrCopyAnnotation(ala);
730  0 newAnnot.annotations = gappedAnnot;
731  0 if (aseq != null)
732    {
733  0 newAnnot.createSequenceMapping(aseq, aseq.findPosition(start), false);
734  0 aseq.addAlignmentAnnotation(newAnnot);
735  0 newAnnot.adjustForAlignment();
736   
737  0 AlignmentAnnotationUtils.replaceAnnotationOnAlignmentWith(
738    newAnnot, newAnnot.label, newAnnot.getCalcId());
739    }
740  0 newAnnots.add(newAnnot);
741   
742    }
743  0 for (SequenceI sq : running.getSeqs())
744    {
745  0 if (!sq.getFeatures().hasFeatures()
746    && (sq.getDBRefs() == null || sq.getDBRefs().size() == 0))
747    {
748  0 continue;
749    }
750  0 running.setTransferSequenceFeatures(true);
751  0 SequenceI seq = running.getSeqNames().get(sq.getName());
752  0 SequenceI dseq;
753  0 ContiguousI seqRange = seq.findPositions(start, end);
754   
755  0 while ((dseq = seq).getDatasetSequence() != null)
756    {
757  0 seq = seq.getDatasetSequence();
758    }
759  0 List<ContiguousI> sourceRange = new ArrayList();
760  0 if (gapMap != null && gapMap.length >= end)
761    {
762  0 int lastcol = start, col = start;
763  0 do
764    {
765  0 if (col == end || !gapMap[col])
766    {
767  0 if (lastcol <= (col - 1))
768    {
769  0 seqRange = seq.findPositions(lastcol, col);
770  0 sourceRange.add(seqRange);
771    }
772  0 lastcol = col + 1;
773    }
774  0 } while (++col <= end);
775    }
776    else
777    {
778  0 sourceRange.add(seq.findPositions(start, end));
779    }
780  0 int i = 0;
781  0 int source_startend[] = new int[sourceRange.size() * 2];
782   
783  0 for (ContiguousI range : sourceRange)
784    {
785  0 source_startend[i++] = range.getBegin();
786  0 source_startend[i++] = range.getEnd();
787    }
788  0 Mapping mp = new Mapping(
789    new MapList(source_startend, new int[]
790    { seq.getStart(), seq.getEnd() }, 1, 1));
791  0 dseq.transferAnnotation(sq, mp);
792   
793    }
794  0 updateOurAnnots(newAnnots);
795    }
796    }
797   
 
798  0 toggle protected void updateOurAnnots(List<AlignmentAnnotation> ourAnnot)
799    {
800  0 List<AlignmentAnnotation> our = ourAnnots;
801  0 ourAnnots = ourAnnot;
802  0 AlignmentI alignment = alignViewport.getAlignment();
803  0 if (our != null)
804    {
805  0 if (our.size() > 0)
806    {
807  0 for (AlignmentAnnotation an : our)
808    {
809  0 if (!ourAnnots.contains(an))
810    {
811    // remove the old annotation
812  0 alignment.deleteAnnotation(an);
813    }
814    }
815    }
816  0 our.clear();
817    }
818   
819    // validate rows and update Alignmment state
820  0 for (AlignmentAnnotation an : ourAnnots)
821    {
822  0 alignViewport.getAlignment().validateAnnotation(an);
823    }
824    // TODO: may need a menu refresh after this
825    // af.setMenusForViewport();
826  0 ap.adjustAnnotationHeight();
827   
828    }
829   
 
830  0 toggle public SequenceAnnotationServiceI getService()
831    {
832  0 return annotService;
833    }
834   
835    }