allows for null Jmol viewer
[jalview.git] / src2 / fr / orsay / lri / varna / models / annotations / TextAnnotation.java
1 /*
2  VARNA is a tool for the automated drawing, visualization and annotation of the secondary structure of RNA, designed as a companion software for web servers and databases.
3  Copyright (C) 2008  Kevin Darty, Alain Denise and Yann Ponty.
4  electronic mail : Yann.Ponty@lri.fr
5  paper mail : LRI, bat 490 Universit� Paris-Sud 91405 Orsay Cedex France
6
7  This file is part of VARNA version 3.1.
8  VARNA version 3.1 is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License
9  as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
10
11  VARNA version 3.1 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
12  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13  See the GNU General Public License for more details.
14
15  You should have received a copy of the GNU General Public License along with VARNA version 3.1.
16  If not, see http://www.gnu.org/licenses.
17  */
18 package fr.orsay.lri.varna.models.annotations;
19
20 import java.awt.Color;
21 import java.awt.Font;
22 import java.awt.geom.Point2D;
23 import java.io.Serializable;
24 import java.text.DecimalFormat;
25 import java.text.NumberFormat;
26 import java.util.ArrayList;
27 import java.util.Collections;
28
29 import javax.xml.transform.sax.TransformerHandler;
30
31 import org.xml.sax.SAXException;
32 import org.xml.sax.helpers.AttributesImpl;
33
34 import fr.orsay.lri.varna.models.rna.ModeleBase;
35 import fr.orsay.lri.varna.VARNAPanel;
36 import fr.orsay.lri.varna.models.VARNAConfigLoader;
37 import fr.orsay.lri.varna.models.rna.ModelBaseStyle;
38 import fr.orsay.lri.varna.models.rna.VARNAPoint;
39 import fr.orsay.lri.varna.utils.XMLUtils;
40
41
42
43 /**
44  * The annotated text model
45  * 
46  * @author Darty@lri.fr
47  * 
48  */
49 public class TextAnnotation implements Serializable {
50
51         /**
52          * 
53          */
54         private static final long serialVersionUID = 465236085501860747L;
55         
56         public enum AnchorType{
57                 POSITION,
58                 BASE,
59                 HELIX,
60                 LOOP
61         };
62         
63
64         public static final String HEADER_TEXT = "TextAnnotation";
65         
66         /**
67          * default text color
68          */
69         public static final Color DEFAULTCOLOR = Color.black;
70         /**
71          * default text font
72          */
73         public static final Font DEFAULTFONT = new Font("Arial", Font.PLAIN, 12);
74
75         private String _text;
76         private AnchorType _typeAnchor;
77         private Color _color;
78         private double _angle;
79         private Object _anchor;
80         private Font _font;
81         
82         public static String XML_ELEMENT_NAME = "textAnnotation";
83         public static String XML_VAR_TYPE_NAME = "type";
84         public static String XML_VAR_COLOR_NAME = "color";
85         public static String XML_VAR_ANGLE_NAME = "angle";
86         public static String XML_VAR_TEXT_NAME = "text";
87
88         public void toXML(TransformerHandler hd) throws SAXException
89         {
90                 AttributesImpl atts = new AttributesImpl();
91                 atts.addAttribute("","",XML_VAR_TYPE_NAME,"CDATA",""+_typeAnchor);
92                 atts.addAttribute("","",XML_VAR_COLOR_NAME,"CDATA",""+XMLUtils.toHTMLNotation(_color));
93                 atts.addAttribute("","",XML_VAR_ANGLE_NAME,"CDATA",""+_angle);
94                 hd.startElement("","",XML_ELEMENT_NAME,atts);
95                 atts.clear();
96                 hd.startElement("","",XML_VAR_TEXT_NAME,atts);
97                 XMLUtils.exportCDATAString(hd, _text);
98                 hd.endElement("","",XML_VAR_TEXT_NAME);
99                 switch (_typeAnchor)
100                 {
101                 case POSITION:
102                         ((VARNAPoint)_anchor).toXML(hd,"pos");
103                         break;
104                 case BASE:
105                         XMLUtils.toXML(hd, (ModeleBase)_anchor);
106                         break;
107                 case HELIX:
108                         XMLUtils.toXML(hd, (ArrayList<ModeleBase>)_anchor);
109                         break;
110                 case LOOP:
111                         XMLUtils.toXML(hd, (ArrayList<ModeleBase>)_anchor);
112                         break;
113                 }               
114                 XMLUtils.toXML(hd, _font);
115                 hd.endElement("","",XML_ELEMENT_NAME);
116         }
117
118         /**
119          * creates an annoted text on a VARNAPanel with the specified text
120          * 
121          * @param texte Textual content of the annotation
122          */
123         public TextAnnotation(String texte) {
124                 _text = texte;
125                 _color = DEFAULTCOLOR;
126                 _font = DEFAULTFONT;
127                 _angle = 0;
128         }
129
130         /**
131          * /** creates an annoted text on a VARNAPanel with the specified text and
132          * is static position
133          * 
134          * @param texte
135          * @param x
136          * @param y
137          */
138         public TextAnnotation(String texte, double x, double y) {
139                 this(texte);
140                 _anchor = new VARNAPoint(x, y);
141                 _typeAnchor = AnchorType.POSITION;
142         }
143
144         /**
145          * creates an annoted text on a VARNAPanel with the specified text fixed to
146          * a base
147          * 
148          * @param texte
149          * @param mb
150          */
151         public TextAnnotation(String texte, ModeleBase mb) {
152                 this(texte);
153                 _anchor = mb;
154                 _typeAnchor = AnchorType.BASE;
155         }
156
157         /**
158          * creates an annoted text on a VARNAPanel with the specified text fixed to
159          * a helix (if type is HELIX) or to a loop (if type is LOOP)
160          * 
161          * @param texte
162          * @param listeBase
163          * @param type
164          * @throws Exception
165          */
166         public TextAnnotation(String texte, ArrayList<ModeleBase> listeBase,
167                         AnchorType type) throws Exception {
168                 this(texte);
169                 _anchor = listeBase;
170
171                 if (type == AnchorType.HELIX)
172                         _typeAnchor = AnchorType.HELIX;
173                 else if (type == AnchorType.LOOP)
174                         _typeAnchor = AnchorType.LOOP;
175                 else
176                         throw new Exception("Bad argument");
177         }
178
179         /**
180          * creates an annoted text from another one
181          * 
182          * @param textAnnotation
183          */
184         public TextAnnotation(TextAnnotation textAnnotation) {
185                 _anchor = textAnnotation.getAncrage();
186                 _font = textAnnotation.getFont();
187                 _text = textAnnotation.getTexte();
188                 _typeAnchor = textAnnotation.getType();
189         }
190
191         /**
192          * 
193          * @return the text
194          */
195         public String getTexte() {
196                 return _text;
197         }
198
199         public void setText(String _texte) {
200                 this._text = _texte;
201         }
202
203         /**
204          * 
205          * @return the font
206          */
207         public Font getFont() {
208                 return _font;
209         }
210
211         public void setFont(Font _font) {
212                 this._font = _font;
213         }
214
215         public Object getAncrage() {
216                 return _anchor;
217         }
218
219         public void setAncrage(ModeleBase mb) {
220                 _anchor = mb;
221                 _typeAnchor = AnchorType.BASE;
222         }
223
224         public void setAncrage(double x, double y) {
225                 _anchor = new VARNAPoint(x, y);
226                 _typeAnchor = AnchorType.POSITION;
227         }
228
229         public void setAncrage(ArrayList<ModeleBase> list, AnchorType type)
230                         throws Exception {
231                 _anchor = list;
232                 if (type == AnchorType.HELIX)
233                         _typeAnchor = AnchorType.HELIX;
234                 else if (type == AnchorType.LOOP)
235                         _typeAnchor = AnchorType.LOOP;
236                 else
237                         throw new Exception("Bad argument");
238         }
239
240         public AnchorType getType() {
241                 return _typeAnchor;
242         }
243
244         public void setType(AnchorType t) {
245                 _typeAnchor = t;
246         }
247
248
249         public Color getColor() {
250                 return _color;
251         }
252
253         public void setColor(Color color) {
254                 this._color = color;
255         }
256
257         
258         public String getHelixDescription()
259         {
260                 ArrayList<ModeleBase> listeBase =  ((ArrayList<ModeleBase>)_anchor);
261                 int minA = Integer.MAX_VALUE,maxA = Integer.MIN_VALUE;
262                 int minB = Integer.MAX_VALUE,maxB = Integer.MIN_VALUE;
263                 for(ModeleBase mb : listeBase)
264                 {
265                         int i = mb.getBaseNumber();
266                         if (mb.getElementStructure()>i)
267                         {
268                                 minA = Math.min(minA, i);
269                                 maxA = Math.max(maxA, i);
270                         }
271                         else
272                         {
273                                 minB = Math.min(minB, i);
274                                 maxB = Math.max(maxB, i);                               
275                         }
276                 }
277                 return "["+minA+","+maxA+"] ["+minB+","+maxB+"]";
278         }
279         
280         public String getLoopDescription()
281         {
282                 ArrayList<ModeleBase> listeBase =  ((ArrayList<ModeleBase>)_anchor);
283                 int min = Integer.MAX_VALUE,max = Integer.MIN_VALUE;
284                 for(ModeleBase mb : listeBase)
285                 {
286                         int i = mb.getBaseNumber();
287                                 min = Math.min(min, i);
288                                 max = Math.max(max, i);
289                 }
290                 return "["+min+","+max+"]";
291         }
292         
293         public String toString() {
294                 String tmp = "["+_text+"] ";
295                 switch (_typeAnchor) {
296                 case POSITION:
297                         NumberFormat formatter = new DecimalFormat(".00"); 
298                         return tmp+" at ("+formatter.format(getCenterPosition().x)+","+formatter.format(getCenterPosition().y)+")";
299                 case BASE:
300                         return tmp+" on base "+((ModeleBase) _anchor).getBaseNumber();
301                 case HELIX:
302                         return tmp+" on helix "+getHelixDescription();
303                 case LOOP:
304                         return tmp+" on loop "+getLoopDescription();
305                 default:
306                         return tmp;
307                 }               
308         }
309
310         /**
311          * 
312          * @return the text position center
313          */
314         public Point2D.Double getCenterPosition() {
315                 switch (_typeAnchor) {
316                 case POSITION:
317                         return ((VARNAPoint) _anchor).toPoint2D();
318                 case BASE:
319                         return ((ModeleBase) _anchor).getCoords();
320                 case HELIX:
321                         return calculLoopHelix();
322                 case LOOP:
323                         return calculLoop();
324                 default:
325                         return new Point2D.Double(0., 0.);
326                 }
327         }
328
329         private Point2D.Double calculLoop() {
330                 ArrayList<ModeleBase> liste = extractedArrayListModeleBaseFromAncrage();
331                 double totalX = 0., totalY = 0.;
332                 for (ModeleBase base : liste) {
333                         totalX += base.getCoords().x;
334                         totalY += base.getCoords().y;
335                 }
336                 return new Point2D.Double(totalX / liste.size(), totalY / liste.size());
337         }
338
339         private Point2D.Double calculLoopHelix() {
340                 ArrayList<ModeleBase> liste = extractedArrayListModeleBaseFromAncrage();
341                 Collections.sort(liste);
342                 double totalX = 0., totalY = 0.;
343                 double num=0.0;
344                 for (int i=0;i<liste.size(); i++) {
345                         ModeleBase base =liste.get(i);
346                         if ((i>0 && (i<liste.size()-1)) || ((liste.size()/2)%2==0))
347                         {
348                                 totalX += base.getCoords().x;
349                                 totalY += base.getCoords().y;
350                                 num += 1;
351                         }
352                 }
353                 return new Point2D.Double(totalX / num, totalY / num);
354         }
355
356         
357         private ArrayList<ModeleBase> extractedArrayListModeleBaseFromAncrage() {
358                 return (ArrayList<ModeleBase>) _anchor;
359         }
360
361         /**
362          * clone a TextAnnotation
363          */
364         public TextAnnotation clone() {
365                 TextAnnotation textAnnot = null;
366                 try {
367                         switch (_typeAnchor) {
368                         case BASE:
369                                 textAnnot = new TextAnnotation(_text, (ModeleBase) _anchor);
370                                 break;
371                         case POSITION:
372                                 textAnnot = new TextAnnotation(_text,
373                                                 ((VARNAPoint) _anchor).x,
374                                                 ((VARNAPoint) _anchor).y);
375                                 break;
376                         case LOOP:
377                                 textAnnot = new TextAnnotation(_text,
378                                                 extractedArrayListModeleBaseFromAncrage(), AnchorType.LOOP);
379                                 break;
380                         case HELIX:
381                                 textAnnot = new TextAnnotation(_text,
382                                                 extractedArrayListModeleBaseFromAncrage(), AnchorType.HELIX);
383                                 break;
384                         default:
385                                 break;
386                         }
387                 } catch (Exception e) {
388                         e.printStackTrace();
389                 }
390                 textAnnot.setFont(_font);
391                 textAnnot.setColor(_color);
392                 return textAnnot;
393
394         }
395
396         /**
397          * copy a textAnnotation
398          * 
399          * @param textAnnotation
400          */
401         public void copy(TextAnnotation textAnnotation) {
402                 _anchor = textAnnotation.getAncrage();
403                 _font = textAnnotation.getFont();
404                 _text = textAnnotation.getTexte();
405                 _typeAnchor = textAnnotation.getType();
406                 _color = textAnnotation.getColor();
407                 _angle = textAnnotation.getAngleInDegres();
408         }
409
410
411         /**
412          * 
413          * @return the angle in degrees
414          */
415         public double getAngleInDegres() {
416                 // if (_typeAncrage == TextAnnotation.HELIX)
417                 // _angle = calculAngleDegres();
418                 return _angle;
419         }
420
421         /**
422          * 
423          * @return the angle in radians
424          */
425         public double getAngleInRadians() {
426                 return (getAngleInDegres() * Math.PI) / 180.;
427         }
428
429         public void setAngleInDegres(double _angle) {
430                 this._angle = _angle;
431         }
432
433         public void setAngleInRadians(double _angle) {
434                 this._angle = _angle * 180 / Math.PI;
435         }
436         
437         
438         public static TextAnnotation parse(String thisAnn, VARNAPanel vp)
439         {
440                 String[] data = thisAnn.split(":");
441
442                 String text = "";
443                 int anchor = -1;
444                 int x = -1;
445                 int y = -1;
446                 TextAnnotation.AnchorType type = TextAnnotation.AnchorType.LOOP;
447                 Font font = TextAnnotation.DEFAULTFONT;
448                 Color color = TextAnnotation.DEFAULTCOLOR;
449                 TextAnnotation ann = null;
450                 try {
451                         if (data.length == 2) {
452                                 text = data[0];
453                                 String[] data2 = data[1].split(",");
454                                 for (int j = 0; j < data2.length; j++) {
455                                         String opt = data2[j];
456                                         String[] data3 = opt.split("=");
457                                         if (data3.length == 2) {
458                                                 String name = data3[0].toLowerCase();
459                                                 String value = data3[1];
460                                                 if (name.equals("type")) {
461                                                         if (value.toUpperCase().equals("H")) {
462                                                                 type = TextAnnotation.AnchorType.HELIX;
463                                                         } else if (value.toUpperCase().equals("L")) {
464                                                                 type = TextAnnotation.AnchorType.LOOP;
465                                                         } else if (value.toUpperCase().equals("P")) {
466                                                                 type = TextAnnotation.AnchorType.POSITION;
467                                                         } else if (value.toUpperCase().equals("B")) {
468                                                                 type = TextAnnotation.AnchorType.BASE;
469                                                         }
470                                                 } else if (name.equals("x")) {
471                                                         x = Integer.parseInt(value);
472                                                 } else if (name.equals("y")) {
473                                                         y = Integer.parseInt(value);
474                                                 } else if (name.equals("anchor")) {
475                                                         anchor = Integer.parseInt(value);
476                                                 } else if (name.equals("size")) {
477                                                         font = font.deriveFont((float) Integer
478                                                                         .parseInt(value));
479                                                 } else if (name.equals("color")) {
480                                                         color = VARNAConfigLoader.getSafeColor(value, color);
481                                                 }
482                                         }
483                                 }
484                                 switch (type) {
485                                 case POSITION:
486                                         if ((x != -1) && (y != -1)) {
487                                                 Point2D.Double p = vp
488                                                                 .panelToLogicPoint(new Point2D.Double(x, y));
489                                                 ann = new TextAnnotation(text, p.x, p.y);
490                                         }
491                                         break;
492                                 case BASE:
493                                         if (anchor != -1) {
494                                                 int index = vp.getRNA().getIndexFromBaseNumber(
495                                                                 anchor);
496                                                 ModeleBase mb = vp.getRNA().get_listeBases()
497                                                                 .get(index);
498                                                 ann = new TextAnnotation(text, mb);
499                                         }
500                                         break;
501                                 case HELIX:
502                                         if (anchor != -1) {
503                                                 ArrayList<ModeleBase> mbl = new ArrayList<ModeleBase>();
504                                                 int index = vp.getRNA().getIndexFromBaseNumber(
505                                                                 anchor);
506                                                 ArrayList<Integer> il = vp.getRNA()
507                                                                 .findHelix(index);
508                                                 for (int k : il) {
509                                                         mbl.add(vp.getRNA().get_listeBases().get(k));
510                                                 }
511                                                 ann = new TextAnnotation(text, mbl, type);
512                                         }
513                                         break;
514                                 case LOOP:
515                                         if (anchor != -1) {
516                                                 ArrayList<ModeleBase> mbl = new ArrayList<ModeleBase>();
517                                                 int index = vp.getRNA().getIndexFromBaseNumber(
518                                                                 anchor);
519                                                 ArrayList<Integer> il = vp.getRNA().findLoop(index);
520                                                 for (int k : il) {
521                                                         mbl.add(vp.getRNA().get_listeBases().get(k));
522                                                 }
523                                                 ann = new TextAnnotation(text, mbl, type);
524                                         }
525                                         break;
526                                 }
527                                 if (ann != null) {
528                                         ann.setColor(color);
529                                         ann.setFont(font);
530                                 }
531                         }
532                 } catch (Exception e) {
533                         System.err.println("Apply Annotations: " + e.toString());
534                 }
535                 return ann;
536                 }
537 }