001package com.hfg.bio.glyco;
002
003
004import java.awt.*;
005import java.awt.geom.Point2D;
006import java.awt.geom.Rectangle2D;
007import java.util.ArrayList;
008import java.util.HashMap;
009import java.util.List;
010import java.util.Map;
011import java.util.Set;
012
013import com.hfg.chem.IonizableGroup;
014import com.hfg.chem.Molecule;
015import com.hfg.svg.SVG;
016import com.hfg.svg.SvgAttr;
017import com.hfg.svg.SvgGroup;
018import com.hfg.svg.SvgNode;
019import com.hfg.svg.SvgPath;
020import com.hfg.util.CompareUtil;
021import com.hfg.util.StringUtil;
022import com.hfg.util.collection.CollectionUtil;
023import com.hfg.util.collection.OrderedSet;
024
025
026//------------------------------------------------------------------------------
027/**
028 Common mammalian polysaccharide structures.
029 Masses / elemental compositions for glycans are for intact structures.
030 A water is removed when a glycan is attached to a protein.
031 <div>
032 @author J. Alex Taylor, hairyfatguy.com
033 </div>
034 */
035//------------------------------------------------------------------------------
036// com.hfg XML/HTML Coding Library
037//
038// This library is free software; you can redistribute it and/or
039// modify it under the terms of the GNU Lesser General Public
040// License as published by the Free Software Foundation; either
041// version 2.1 of the License, or (at your option) any later version.
042//
043// This library is distributed in the hope that it will be useful,
044// but WITHOUT ANY WARRANTY; without even the implied warranty of
045// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
046// Lesser General Public License for more details.
047//
048// You should have received a copy of the GNU Lesser General Public
049// License along with this library; if not, write to the Free Software
050// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
051//
052// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com
053// jataylor@hairyfatguy.com
054//------------------------------------------------------------------------------
055
056public class Glycan extends Molecule
057{
058   //##########################################################################
059   // PUBLIC FIELDS
060   //##########################################################################
061
062   // Complex
063   public static Glycan A2G0 = new Glycan("A2G0");
064   public static Glycan A2G0F = new Glycan("A2G0F");
065   public static Glycan A2G1 = new Glycan("A2G1");
066   public static Glycan A2G1F = new Glycan("A2G1F");
067   public static Glycan A2G2 = new Glycan("A2G2");
068   public static Glycan A2G2F = new Glycan("A2G2F");
069   public static Glycan A2S1G0F = new Glycan("A2S1G0F");
070   public static Glycan A2S1G1  = new Glycan("A2S1G1");
071   public static Glycan A2S1G1F = new Glycan("A2S1G1F");
072   public static Glycan A2S2 = new Glycan("A2S2");
073   public static Glycan A3G0 = new Glycan("A3G0");
074   public static Glycan A3G0F = new Glycan("A3G0F");
075   public static Glycan A3G1 = new Glycan("A3G1");
076   public static Glycan A3G1F = new Glycan("A3G1F");
077   public static Glycan A3G2 = new Glycan("A3G2");
078   public static Glycan A3G2F = new Glycan("A3G2F");
079   public static Glycan A3S1G2 = new Glycan("A3S1G2");
080   public static Glycan A3G3 = new Glycan("A3G3");
081   public static Glycan A3S3 = new Glycan("A3S3");
082   public static Glycan A3S2G1 = new Glycan("A3S2G1");
083
084   // Hybrid
085   public static Glycan M3F = new Glycan("M3F");  // Core-F
086   public static Glycan A1G0 = new Glycan("A1G0");
087   public static Glycan A1G0F = new Glycan("A1G0F");
088   public static Glycan A1G0M4F = new Glycan("A1G0M4F");
089   public static Glycan A1G1F = new Glycan("A1G1F");
090   public static Glycan A1G1M4F = new Glycan("A1G1M4F");
091   public static Glycan A1G1M5F = new Glycan("A1G1M5F");
092   public static Glycan A1S1M4F = new Glycan("A1S1M4F");
093   public static Glycan A2G0M4F = new Glycan("A2G0M4F");
094   public static Glycan A2S2F = new Glycan("A2S2F");
095   public static Glycan A3G3F = new Glycan("A3G3F");
096   public static Glycan A3S1G2F = new Glycan("A3S1G2F");
097   public static Glycan A3S2G1F = new Glycan("A3S2G1F");
098   public static Glycan A3S3F = new Glycan("A3S3F");
099
100   // High-Mannose
101   public static Glycan M3 = new Glycan("M3");  // Core
102   public static Glycan M4 = new Glycan("M4");
103   public static Glycan M5 = new Glycan("M5");
104   public static Glycan M6 = new Glycan("M6");
105   public static Glycan M7 = new Glycan("M7");
106   public static Glycan M8 = new Glycan("M8");
107   public static Glycan M9 = new Glycan("M9");
108
109
110   //##########################################################################
111   // PRIVATE FIELDS
112   //##########################################################################
113
114   private String mName;
115   private String mAbbrev;
116   private Map<Monosaccharide, Integer> mMonosaccharideCountMap = new HashMap<>(5);
117
118   // This declaration has to come before the public constants below.
119   private static Set<Glycan> sValues = new OrderedSet<>();
120
121   static
122   {
123
124      M3.add(Monosaccharide.GlcNAc, 2)
125            .add(Monosaccharide.Mannose, 3)
126            .lock()
127            .register();
128
129      A2G0.add(Monosaccharide.GlcNAc, 4)
130            .add(Monosaccharide.Mannose, 3)
131            .lock()
132            .register();
133
134      A2G0F.add(Monosaccharide.GlcNAc, 4)
135            .add(Monosaccharide.Mannose, 3)
136            .add(Monosaccharide.Fucose, 1)
137            .lock()
138            .register();
139
140      A2G1.add(Monosaccharide.GlcNAc, 4)
141            .add(Monosaccharide.Mannose, 3)
142            .add(Monosaccharide.Hexose, 1)
143            .lock()
144            .register();
145
146      A2G1F.add(Monosaccharide.GlcNAc, 4)
147            .add(Monosaccharide.Mannose, 3)
148            .add(Monosaccharide.Hexose, 1)
149            .add(Monosaccharide.Fucose, 1)
150            .lock()
151            .register();
152
153      A2G2.add(Monosaccharide.GlcNAc, 4)
154            .add(Monosaccharide.Mannose, 3)
155            .add(Monosaccharide.Hexose, 2)
156            .lock()
157            .register();
158
159      A2G2F.add(Monosaccharide.GlcNAc, 4)
160            .add(Monosaccharide.Mannose, 3)
161            .add(Monosaccharide.Hexose, 2)
162            .add(Monosaccharide.Fucose, 1)
163            .lock()
164            .register();
165
166      A2S1G0F.add(Monosaccharide.GlcNAc, 4)
167            .add(Monosaccharide.Mannose, 3)
168            .add(Monosaccharide.Hexose, 1)
169            .add(Monosaccharide.NeuAc, 1)
170            .lock()
171            .register();
172
173      A2S1G1.add(Monosaccharide.GlcNAc, 4)
174            .add(Monosaccharide.Mannose, 3)
175            .add(Monosaccharide.Hexose, 2)
176            .add(Monosaccharide.NeuAc, 1)
177            .lock()
178            .register();
179
180      A2S1G1F.add(Monosaccharide.GlcNAc, 4)
181            .add(Monosaccharide.Mannose, 3)
182            .add(Monosaccharide.Hexose, 2)
183            .add(Monosaccharide.Fucose, 1)
184            .add(Monosaccharide.NeuAc, 1)
185            .lock()
186            .register();
187
188      A2S2.add(Monosaccharide.GlcNAc, 4)
189            .add(Monosaccharide.Mannose, 3)
190            .add(Monosaccharide.Hexose, 2)
191            .add(Monosaccharide.NeuAc, 2)
192            .lock()
193            .register();
194
195      A3G0.add(Monosaccharide.GlcNAc, 5)
196            .add(Monosaccharide.Mannose, 3)
197            .lock()
198            .register();
199
200      A3G0F.add(Monosaccharide.GlcNAc, 5)
201            .add(Monosaccharide.Mannose, 3)
202            .add(Monosaccharide.Fucose, 1)
203            .lock()
204            .register();
205
206      A3G1.add(Monosaccharide.GlcNAc, 5)
207            .add(Monosaccharide.Mannose, 3)
208            .add(Monosaccharide.Hexose, 1)
209            .lock()
210            .register();
211
212      A3G1F.add(Monosaccharide.GlcNAc, 5)
213            .add(Monosaccharide.Mannose, 3)
214            .add(Monosaccharide.Hexose, 1)
215            .add(Monosaccharide.Fucose, 1)
216            .lock()
217            .register();
218
219      A3G2.add(Monosaccharide.GlcNAc, 5)
220            .add(Monosaccharide.Mannose, 3)
221            .add(Monosaccharide.Hexose, 2)
222            .lock()
223            .register();
224
225      A3G2F.add(Monosaccharide.GlcNAc, 5)
226            .add(Monosaccharide.Mannose, 3)
227            .add(Monosaccharide.Hexose, 2)
228            .add(Monosaccharide.Fucose, 1)
229            .lock()
230            .register();
231
232      A3S1G2.add(Monosaccharide.GlcNAc, 5)
233            .add(Monosaccharide.Mannose, 3)
234            .add(Monosaccharide.Hexose, 3)
235            .add(Monosaccharide.NeuAc, 1)
236            .lock()
237            .register();
238
239      A3S2G1.add(Monosaccharide.GlcNAc, 5)
240            .add(Monosaccharide.Mannose, 3)
241            .add(Monosaccharide.Hexose, 3)
242            .add(Monosaccharide.NeuAc, 2)
243            .lock()
244            .register();
245
246      A3G3.add(Monosaccharide.GlcNAc, 5)
247            .add(Monosaccharide.Mannose, 3)
248            .add(Monosaccharide.Hexose, 3)
249            .lock()
250            .register();
251
252      A3S3.add(Monosaccharide.GlcNAc, 5)
253            .add(Monosaccharide.Mannose, 3)
254            .add(Monosaccharide.Hexose, 3)
255            .add(Monosaccharide.NeuAc, 3)
256            .lock()
257            .register();
258
259
260
261      M3F.add(Monosaccharide.GlcNAc, 2)
262             .add(Monosaccharide.Mannose, 3)
263             .add(Monosaccharide.Fucose, 1)
264             .lock()
265             .register();
266
267      A1G0.add(Monosaccharide.GlcNAc, 3)
268            .add(Monosaccharide.Mannose, 3)
269            .lock()
270            .register();
271
272      A1G0F.add(Monosaccharide.GlcNAc, 3)
273            .add(Monosaccharide.Mannose, 3)
274            .add(Monosaccharide.Fucose, 1)
275            .lock()
276            .register();
277
278      A1G0M4F.add(Monosaccharide.GlcNAc, 3)
279            .add(Monosaccharide.Mannose, 4)
280            .add(Monosaccharide.Fucose, 1)
281            .lock()
282            .register();
283
284      A1G1F.add(Monosaccharide.GlcNAc, 3)
285            .add(Monosaccharide.Mannose, 3)
286            .add(Monosaccharide.Hexose, 1)
287            .add(Monosaccharide.Fucose, 1)
288            .lock()
289            .register();
290
291      A1G1M4F.add(Monosaccharide.GlcNAc, 3)
292            .add(Monosaccharide.Mannose, 4)
293            .add(Monosaccharide.Hexose, 1)
294            .add(Monosaccharide.Fucose, 1)
295            .lock()
296            .register();
297
298      A1G1M5F.add(Monosaccharide.GlcNAc, 3)
299            .add(Monosaccharide.Mannose, 5)
300            .add(Monosaccharide.Hexose, 1)
301            .add(Monosaccharide.Fucose, 1)
302            .lock()
303            .register();
304
305      A1S1M4F.add(Monosaccharide.GlcNAc, 3)
306            .add(Monosaccharide.Mannose, 4)
307            .add(Monosaccharide.Hexose, 1)
308            .add(Monosaccharide.Fucose, 1)
309            .add(Monosaccharide.NeuAc, 1)
310            .lock()
311            .register();
312
313      A2G0M4F.add(Monosaccharide.GlcNAc, 4)
314            .add(Monosaccharide.Mannose, 4)
315            .add(Monosaccharide.Fucose, 1)
316            .lock()
317            .register();
318
319      A2S2F.add(Monosaccharide.GlcNAc, 4)
320            .add(Monosaccharide.Mannose, 3)
321            .add(Monosaccharide.Hexose, 2)
322            .add(Monosaccharide.Fucose, 1)
323            .add(Monosaccharide.NeuAc, 2)
324            .lock()
325            .register();
326
327      A3G3F.add(Monosaccharide.GlcNAc, 5)
328            .add(Monosaccharide.Mannose, 3)
329            .add(Monosaccharide.Hexose, 3)
330            .add(Monosaccharide.Fucose, 1)
331            .lock()
332            .register();
333
334      A3S1G2F.add(Monosaccharide.GlcNAc, 5)
335            .add(Monosaccharide.Mannose, 3)
336            .add(Monosaccharide.Hexose, 3)
337            .add(Monosaccharide.Fucose, 1)
338            .add(Monosaccharide.NeuAc, 1)
339            .lock()
340            .register();
341
342      A3S2G1F.add(Monosaccharide.GlcNAc, 5)
343            .add(Monosaccharide.Mannose, 3)
344            .add(Monosaccharide.Hexose, 3)
345            .add(Monosaccharide.Fucose, 1)
346            .add(Monosaccharide.NeuAc, 2)
347            .lock()
348            .register();
349
350      A3S3F.add(Monosaccharide.GlcNAc, 5)
351            .add(Monosaccharide.Mannose, 3)
352            .add(Monosaccharide.Hexose, 3)
353            .add(Monosaccharide.Fucose, 1)
354            .add(Monosaccharide.NeuAc, 3)
355            .lock()
356            .register();
357
358
359
360
361      M4.add(Monosaccharide.GlcNAc, 2)
362            .add(Monosaccharide.Mannose, 4)
363            .lock()
364            .register();
365
366      M5.add(Monosaccharide.GlcNAc, 2)
367            .add(Monosaccharide.Mannose, 5)
368            .lock()
369            .register();
370
371      M6.add(Monosaccharide.GlcNAc, 2)
372            .add(Monosaccharide.Mannose, 6)
373            .lock()
374            .register();
375
376      M7.add(Monosaccharide.GlcNAc, 2)
377            .add(Monosaccharide.Mannose, 7)
378            .lock()
379            .register();
380
381      M8.add(Monosaccharide.GlcNAc, 2)
382            .add(Monosaccharide.Mannose, 8)
383            .lock()
384            .register();
385
386      M9.add(Monosaccharide.GlcNAc, 2)
387            .add(Monosaccharide.Mannose, 9)
388            .lock()
389            .register();
390   }
391
392   //##########################################################################
393   // CONSTRUCTORS
394   //##########################################################################
395
396   //--------------------------------------------------------------------------
397   private Glycan(String inName)
398   {
399      mName   = inName;
400      mAbbrev = inName;
401   }
402
403   //##########################################################################
404   // PUBLIC METHODS
405   //##########################################################################
406
407   //--------------------------------------------------------------------------
408   public static Glycan valueOf(String inString)
409   {
410      Glycan value = null;
411
412      if (StringUtil.isSet(inString))
413      {
414         for (Glycan glycan : sValues)
415         {
416            if (glycan.name().equalsIgnoreCase(inString)
417                  || glycan.getAbbrev().equals(inString))
418            {
419               value = glycan;
420               break;
421            }
422         }
423      }
424
425      return value;
426   }
427
428   //--------------------------------------------------------------------------
429   public static Glycan[] values()
430   {
431      return sValues.toArray(new Glycan[sValues.size()]);
432   }
433
434
435   //--------------------------------------------------------------------------
436   /**
437    Puts the Glycan into the Set of unique Glycans returned by Glycan.values().
438    */
439   public void register()
440   {
441      if (! isLocked())
442      {
443         throw new RuntimeException("Only locked Glycans can be added to the values list!");
444      }
445
446      sValues.add(this);
447   }
448
449   //--------------------------------------------------------------------------
450   @Override
451   public int compareTo(Object inObj2)
452   {
453      int result = -1;
454
455      if (inObj2 != null)
456      {
457         if (inObj2 instanceof Glycan)
458         {
459            Glycan glycan2 = (Glycan) inObj2;
460
461            result = CompareUtil.compare(name(), glycan2.name());
462         }
463         else
464         {
465            result = CompareUtil.compare(hashCode(), inObj2.hashCode());
466         }
467      }
468
469      return result;
470   }
471
472   //--------------------------------------------------------------------------
473   @Override
474   public Glycan lock()
475   {
476      return (Glycan) super.lock();
477   }
478
479   //--------------------------------------------------------------------------
480   public String name()
481   {
482      return mName;
483   }
484
485   //--------------------------------------------------------------------------
486   public String getAbbrev()
487   {
488      return mAbbrev;
489   }
490
491   //--------------------------------------------------------------------------
492   @Override
493   public String toString()
494   {
495      return getAbbrev();
496   }
497
498   //--------------------------------------------------------------------------
499   public SVG toSVG()
500   {
501      SVG svg = new SVG();
502
503      SvgGroup backbone = svg.addGroup().addStyle(SvgAttr.stroke + ": #000000");
504
505      // Start with the M3 core
506      SvgGroup core = svg.addGroup();
507
508      SvgNode glcNAc1 = Monosaccharide.GlcNAc.toSVGNode().setPosition(new Point2D.Float(130, 27));
509      core.addSubtag(glcNAc1);
510
511      SvgNode glcNAc2 = Monosaccharide.GlcNAc.toSVGNode().setPosition(new Point2D.Float(110, 27));
512      core.addSubtag(glcNAc2);
513      backbone.addLine(glcNAc1.getCenterPoint(), glcNAc2.getCenterPoint());
514
515      SvgNode man1 = Monosaccharide.Mannose.toSVGNode().setPosition(new Point2D.Float(90, 27));
516      core.addSubtag(man1);
517      backbone.addLine(glcNAc2.getCenterPoint(), man1.getCenterPoint());
518
519      SvgNode man2 = Monosaccharide.Mannose.toSVGNode().setPosition(new Point2D.Float(70, 12));
520      core.addSubtag(man2);
521      backbone.addLine(man1.getCenterPoint(), man2.getCenterPoint());
522
523      SvgNode man3 = Monosaccharide.Mannose.toSVGNode().setPosition(new Point2D.Float(70, 37));
524      core.addSubtag(man3);
525      backbone.addLine(man1.getCenterPoint(), man3.getCenterPoint());
526
527      if (name().endsWith("F"))
528      {
529         SvgNode svgNode = Monosaccharide.Fucose.toSVGNode().setPosition(new Point2D.Float(130, 7));
530         svg.addSubtag(svgNode);
531         backbone.addLine(glcNAc1.getCenterPoint(), svgNode.getCenterPoint());
532      }
533
534      int mannoseCount = mMonosaccharideCountMap.containsKey(Monosaccharide.Mannose) ? mMonosaccharideCountMap.get(Monosaccharide.Mannose) : 0;
535      int glcNAcCount = mMonosaccharideCountMap.containsKey(Monosaccharide.GlcNAc) ? mMonosaccharideCountMap.get(Monosaccharide.GlcNAc) : 0;
536      int hexoseCount = mMonosaccharideCountMap.containsKey(Monosaccharide.Hexose) ? mMonosaccharideCountMap.get(Monosaccharide.Hexose) : 0;
537      int sialicAcidCount = mMonosaccharideCountMap.containsKey(Monosaccharide.NeuAc) ? mMonosaccharideCountMap.get(Monosaccharide.NeuAc) : 0;
538
539      Rectangle2D man2bbox = man2.getBoundsBox();
540      Rectangle2D man3bbox = man3.getBoundsBox();
541
542      if (3 == mannoseCount)
543      {
544         if (3 == glcNAcCount)
545         {
546            SvgPath curlyBrace = SvgPath.generateCurlyBracket(new Point2D.Double(man2bbox.getX() - 4, man2bbox.getY()),
547                                                              new Point2D.Double(man3bbox.getX() - 4, (man3bbox.getY() + man3bbox.getHeight())), 10);
548            svg.addSubtag(curlyBrace);
549
550            Rectangle2D curlyBraceBBox = curlyBrace.getBoundsBox();
551
552            svg.addSubtag(Monosaccharide.GlcNAc.toSVGNode().setPosition(new Point2D.Double(curlyBraceBBox.getX() - 14, (curlyBraceBBox.getY() + curlyBraceBBox.getHeight() / 2) - 5)));
553         }
554         else if (4 == glcNAcCount)
555         {
556            SvgNode glcNac3 = Monosaccharide.GlcNAc.toSVGNode().setPosition(new Point2D.Double(man2bbox.getX() - 20, man2bbox.getY()));
557            svg.addSubtag(glcNac3);
558            backbone.addLine(man2.getCenterPoint(), glcNac3.getCenterPoint());
559
560            SvgNode glcNac4 = Monosaccharide.GlcNAc.toSVGNode().setPosition(new Point2D.Double(man3bbox.getX() - 20, man3bbox.getY()));
561            svg.addSubtag(glcNac4);
562            backbone.addLine(man3.getCenterPoint(), glcNac4.getCenterPoint());
563
564            Rectangle2D glcNac3bbox = glcNac3.getBoundsBox();
565            Rectangle2D glcNac4bbox = glcNac4.getBoundsBox();
566
567            if (1 == hexoseCount)
568            {
569               SvgPath curlyBrace = SvgPath.generateCurlyBracket(new Point2D.Double(glcNac3bbox.getX() - 4, glcNac3bbox.getY()),
570                                                                 new Point2D.Double(glcNac4bbox.getX() - 4, (glcNac4bbox.getY() + glcNac4bbox.getHeight())), 10);
571               svg.addSubtag(curlyBrace);
572
573               Rectangle2D curlyBraceBBox = curlyBrace.getBoundsBox();
574
575               SvgNode hex = Monosaccharide.Hexose.toSVGNode().setPosition(new Point2D.Double(curlyBraceBBox.getX() - 14, (curlyBraceBBox.getY() + curlyBraceBBox.getHeight() / 2) - 5));
576               svg.addSubtag(hex);
577
578               if (sialicAcidCount > 0)
579               {
580                  Rectangle2D hexBBox = hex.getBoundsBox();
581
582                  SvgNode sialicAcid = Monosaccharide.NeuAc.toSVGNode().setPosition(new Point2D.Double(hexBBox.getX() - 20, hexBBox.getY()));
583                  svg.addSubtag(sialicAcid);
584                  backbone.addLine(hex.getCenterPoint(), sialicAcid.getCenterPoint());
585               }
586            }
587            else if (2 == hexoseCount)
588            {
589               SvgNode hex1 = Monosaccharide.Hexose.toSVGNode().setPosition(new Point2D.Double(glcNac3bbox.getX() - 20, glcNac3bbox.getY()));
590               svg.addSubtag(hex1);
591               backbone.addLine(glcNac3.getCenterPoint(), hex1.getCenterPoint());
592
593               SvgNode hex2 = Monosaccharide.Hexose.toSVGNode().setPosition(new Point2D.Double(glcNac4bbox.getX() - 20, glcNac4bbox.getY()));
594               svg.addSubtag(hex2);
595               backbone.addLine(glcNac4.getCenterPoint(), hex2.getCenterPoint());
596
597               Rectangle2D hex1bbox = hex1.getBoundsBox();
598               Rectangle2D hex2bbox = hex2.getBoundsBox();
599
600               if (1 == sialicAcidCount)
601               {
602                  SvgPath curlyBrace = SvgPath.generateCurlyBracket(new Point2D.Double(hex1bbox.getX() - 4, hex1bbox.getY()),
603                                                                    new Point2D.Double(hex2bbox.getX() - 4, (hex2bbox.getY() + hex2bbox.getHeight())), 10);
604                  svg.addSubtag(curlyBrace);
605
606                  Rectangle curlyBraceBBox = curlyBrace.getBoundsBox();
607
608                  svg.addSubtag(Monosaccharide.NeuAc.toSVGNode().setPosition(new Point2D.Double(curlyBraceBBox.getX() - 14, (curlyBraceBBox.getY() + curlyBraceBBox.getHeight() / 2) - 5)));
609               }
610               else if (2 == sialicAcidCount)
611               {
612                  SvgNode sialicAcid1 = Monosaccharide.NeuAc.toSVGNode().setPosition(new Point2D.Double(hex1bbox.getX() - 20, hex1bbox.getY()));
613                  svg.addSubtag(sialicAcid1);
614                  backbone.addLine(hex1.getCenterPoint(), sialicAcid1.getCenterPoint());
615
616                  SvgNode sialicAcid2 = Monosaccharide.NeuAc.toSVGNode().setPosition(new Point2D.Double(hex2bbox.getX() - 20, hex2bbox.getY()));
617                  svg.addSubtag(sialicAcid2);
618                  backbone.addLine(hex2.getCenterPoint(), sialicAcid2.getCenterPoint());
619               }
620            }
621         }
622         else if (5 == glcNAcCount)
623         {
624            SvgNode glcNac3 = Monosaccharide.GlcNAc.toSVGNode().setPosition(new Point2D.Double(man2bbox.getX() - 20, man2bbox.getY() - 10));
625            svg.addSubtag(glcNac3);
626            backbone.addLine(man2.getCenterPoint(), glcNac3.getCenterPoint());
627
628            SvgNode glcNac4 = Monosaccharide.GlcNAc.toSVGNode().setPosition(new Point2D.Double(man3bbox.getX() - 20, man2bbox.getY() + 7));
629            svg.addSubtag(glcNac4);
630            backbone.addLine(man2.getCenterPoint(), glcNac4.getCenterPoint());
631
632            SvgNode glcNac5 = Monosaccharide.GlcNAc.toSVGNode().setPosition(new Point2D.Double(man3bbox.getX() - 20, man3bbox.getY()));
633            svg.addSubtag(glcNac5);
634            backbone.addLine(man3.getCenterPoint(), glcNac5.getCenterPoint());
635
636            Rectangle2D glcNac3bbox = glcNac3.getBoundsBox();
637            Rectangle2D glcNac4bbox = glcNac4.getBoundsBox();
638            Rectangle2D glcNac5bbox = glcNac5.getBoundsBox();
639
640            if (hexoseCount >= 1
641                && hexoseCount <= 2)
642            {
643               SvgPath curlyBrace = SvgPath.generateCurlyBracket(new Point2D.Double(glcNac3bbox.getX() - 4, glcNac3bbox.getY()),
644                                                                 new Point2D.Double(glcNac5bbox.getX() - 4, (glcNac5bbox.getY() + glcNac5bbox.getHeight())), 10);
645               svg.addSubtag(curlyBrace);
646
647               Rectangle2D curlyBraceBBox = curlyBrace.getBoundsBox();
648
649               if (1 == hexoseCount)
650               {
651                  SvgNode hex = Monosaccharide.Hexose.toSVGNode().setPosition(new Point2D.Double(curlyBraceBBox.getX() - 14, (curlyBraceBBox.getY() + curlyBraceBBox.getHeight() / 2) - 5));
652                  svg.addSubtag(hex);
653
654                  if (sialicAcidCount > 0)
655                  {
656                     Rectangle2D hexBBox = hex.getBoundsBox();
657
658                     SvgNode sialicAcid = Monosaccharide.NeuAc.toSVGNode().setPosition(new Point2D.Double(hexBBox.getX() - 20, hexBBox.getY()));
659                     svg.addSubtag(sialicAcid);
660                     backbone.addLine(hex.getCenterPoint(), sialicAcid.getCenterPoint());
661                  }
662               }
663               else
664               {
665                  SvgNode hex1 = Monosaccharide.Hexose.toSVGNode().setPosition(new Point2D.Double(curlyBraceBBox.getX() - 14, (curlyBraceBBox.getY() + curlyBraceBBox.getHeight() * 0.25) - 5));
666                  svg.addSubtag(hex1);
667
668                  SvgNode hex2 = Monosaccharide.Hexose.toSVGNode().setPosition(new Point2D.Double(curlyBraceBBox.getX() - 14, (curlyBraceBBox.getY() + curlyBraceBBox.getHeight() * 0.75) - 5));
669                  svg.addSubtag(hex2);
670
671                  Rectangle2D hex1BBox = hex1.getBoundsBox();
672                  Rectangle2D hex2BBox = hex2.getBoundsBox();
673
674                  if (sialicAcidCount > 0)
675                  {
676                     SvgNode sialicAcid = Monosaccharide.NeuAc.toSVGNode().setPosition(new Point2D.Double(hex1BBox.getX() - 20, hex1BBox.getY()));
677                     svg.addSubtag(sialicAcid);
678                     backbone.addLine(hex1.getCenterPoint(), sialicAcid.getCenterPoint());
679
680                     if (2 == sialicAcidCount)
681                     {
682                        SvgNode sialicAcid2 = Monosaccharide.NeuAc.toSVGNode().setPosition(new Point2D.Double(hex2BBox.getX() - 20, hex2BBox.getY()));
683                        svg.addSubtag(sialicAcid2);
684                        backbone.addLine(hex2.getCenterPoint(), sialicAcid2.getCenterPoint());
685                     }
686                  }
687               }
688            }
689            else if (3 == hexoseCount)
690            {
691               SvgNode hex1 = Monosaccharide.Hexose.toSVGNode().setPosition(new Point2D.Double(glcNac3bbox.getX() - 20, glcNac3bbox.getY()));
692               svg.addSubtag(hex1);
693               backbone.addLine(glcNac3.getCenterPoint(), hex1.getCenterPoint());
694
695               SvgNode hex2 = Monosaccharide.Hexose.toSVGNode().setPosition(new Point2D.Double(glcNac4bbox.getX() - 20, glcNac4bbox.getY()));
696               svg.addSubtag(hex2);
697               backbone.addLine(glcNac4.getCenterPoint(), hex2.getCenterPoint());
698
699               SvgNode hex3 = Monosaccharide.Hexose.toSVGNode().setPosition(new Point2D.Double(glcNac5bbox.getX() - 20, glcNac5bbox.getY()));
700               svg.addSubtag(hex3);
701               backbone.addLine(glcNac5.getCenterPoint(), hex3.getCenterPoint());
702
703               Rectangle2D hex1bbox = hex1.getBoundsBox();
704               Rectangle2D hex2bbox = hex2.getBoundsBox();
705               Rectangle2D hex3bbox = hex3.getBoundsBox();
706
707               if (sialicAcidCount >= 1
708                     && sialicAcidCount <= 2)
709               {
710                  SvgPath curlyBrace = SvgPath.generateCurlyBracket(new Point2D.Double(hex1bbox.getX() - 4, hex1bbox.getY()),
711                                                                    new Point2D.Double(hex3bbox.getX() - 4, (hex3bbox.getY() + hex3bbox.getHeight())), 10);
712                  svg.addSubtag(curlyBrace);
713
714                  Rectangle2D curlyBraceBBox = curlyBrace.getBoundsBox();
715
716                  if (1 == sialicAcidCount)
717                  {
718                     svg.addSubtag(Monosaccharide.NeuAc.toSVGNode().setPosition(new Point2D.Double(curlyBraceBBox.getX() - 14, (curlyBraceBBox.getY() + curlyBraceBBox.getHeight() / 2) - 5)));
719                  }
720                  else if (2 == sialicAcidCount)
721                  {
722                     SvgNode sialicAcid1 = Monosaccharide.NeuAc.toSVGNode().setPosition(new Point2D.Double(curlyBraceBBox.getX() - 14, (curlyBraceBBox.getY() + curlyBraceBBox.getHeight() * 0.25) - 5));
723                     svg.addSubtag(sialicAcid1);
724
725                     SvgNode sialicAcid2 = Monosaccharide.NeuAc.toSVGNode().setPosition(new Point2D.Double(curlyBraceBBox.getX() - 14, (curlyBraceBBox.getY() + curlyBraceBBox.getHeight() * 0.75) - 5));
726                     svg.addSubtag(sialicAcid2);
727                  }
728               }
729               else if (3 == sialicAcidCount)
730               {
731                  SvgNode sialicAcid1 = Monosaccharide.NeuAc.toSVGNode().setPosition(new Point2D.Double(hex1bbox.getX() - 20, hex1bbox.getY()));
732                  svg.addSubtag(sialicAcid1);
733                  backbone.addLine(hex1.getCenterPoint(), sialicAcid1.getCenterPoint());
734
735                  SvgNode sialicAcid2 = Monosaccharide.NeuAc.toSVGNode().setPosition(new Point2D.Double(hex2bbox.getX() - 20, hex2bbox.getY()));
736                  svg.addSubtag(sialicAcid2);
737                  backbone.addLine(hex2.getCenterPoint(), sialicAcid2.getCenterPoint());
738
739                  SvgNode sialicAcid3 = Monosaccharide.NeuAc.toSVGNode().setPosition(new Point2D.Double(hex3bbox.getX() - 20, hex3bbox.getY()));
740                  svg.addSubtag(sialicAcid3);
741                  backbone.addLine(hex3.getCenterPoint(), sialicAcid3.getCenterPoint());
742               }
743            }
744         }
745      }
746      else if (4 == mannoseCount)
747      {
748         if (glcNAcCount > 2)
749         {
750            SvgNode man4 = Monosaccharide.Mannose.toSVGNode().setPosition(new Point2D.Float(50, 7));
751            svg.addSubtag(man4);
752            backbone.addLine(man2.getCenterPoint(), man4.getCenterPoint());
753
754            SvgNode glcNac3 = Monosaccharide.GlcNAc.toSVGNode().setPosition(new Point2D.Float(50, 37));
755            svg.addSubtag(glcNac3);
756            backbone.addLine(man3.getCenterPoint(), glcNac3.getCenterPoint());
757
758            if (hexoseCount > 0)
759            {
760               SvgNode hex = Monosaccharide.Hexose.toSVGNode().setPosition(new Point2D.Float(30, 37));
761               svg.addSubtag(hex);
762               backbone.addLine(glcNac3.getCenterPoint(), hex.getCenterPoint());
763
764               if (sialicAcidCount > 0)
765               {
766                  SvgNode sialicAcid = Monosaccharide.NeuAc.toSVGNode().setPosition(new Point2D.Float(10, 37));
767                  svg.addSubtag(sialicAcid);
768                  backbone.addLine(hex.getCenterPoint(), sialicAcid.getCenterPoint());
769               }
770            }
771         }
772         else
773         {
774            SvgPath curlyBrace = SvgPath.generateCurlyBracket(new Point2D.Double(man2bbox.getX() - 4, man2bbox.getY()),
775                                                              new Point2D.Double(man3bbox.getX() - 4, (man3bbox.getY() + man3bbox.getHeight())), 10);
776            svg.addSubtag(curlyBrace);
777
778            Rectangle2D curlyBraceBBox = curlyBrace.getBoundsBox();
779
780            svg.addSubtag(Monosaccharide.Mannose.toSVGNode().setPosition(new Point2D.Double(curlyBraceBBox.getX() - 14, (curlyBraceBBox.getY() + curlyBraceBBox.getHeight() / 2) - 5)));
781         }
782      }
783      else if (5 == mannoseCount)
784      {
785         SvgNode man4 = Monosaccharide.Mannose.toSVGNode().setPosition(new Point2D.Float(50, 2));
786         svg.addSubtag(man4);
787         backbone.addLine(man2.getCenterPoint(), man4.getCenterPoint());
788
789         SvgNode man5 = Monosaccharide.Mannose.toSVGNode().setPosition(new Point2D.Float(50, 19));
790         svg.addSubtag(man5);
791         backbone.addLine(man2.getCenterPoint(), man5.getCenterPoint());
792
793         if (glcNAcCount > 2)
794         {
795            SvgNode glcNac3 = Monosaccharide.GlcNAc.toSVGNode().setPosition(new Point2D.Float(50, 37));
796            svg.addSubtag(glcNac3);
797            backbone.addLine(man3.getCenterPoint(), glcNac3.getCenterPoint());
798
799            if (hexoseCount > 0)
800            {
801               SvgNode hex = Monosaccharide.Hexose.toSVGNode().setPosition(new Point2D.Float(30, 37));
802               svg.addSubtag(hex);
803               backbone.addLine(glcNac3.getCenterPoint(), hex.getCenterPoint());
804
805               if (sialicAcidCount > 0)
806               {
807                  SvgNode sialicAcid = Monosaccharide.NeuAc.toSVGNode().setPosition(new Point2D.Float(10, 37));
808                  svg.addSubtag(sialicAcid);
809                  backbone.addLine(hex.getCenterPoint(), sialicAcid.getCenterPoint());
810               }
811            }
812         }
813      }
814      else if (6 <= mannoseCount)
815      {
816         SvgNode man4 = Monosaccharide.Mannose.toSVGNode().setPosition(new Point2D.Float(50, 2));
817         svg.addSubtag(man4);
818         backbone.addLine(man2.getCenterPoint(), man4.getCenterPoint());
819
820         SvgNode man5 = Monosaccharide.Mannose.toSVGNode().setPosition(new Point2D.Float(50, 19));
821         svg.addSubtag(man5);
822         backbone.addLine(man2.getCenterPoint(), man5.getCenterPoint());
823
824         SvgNode man6 = Monosaccharide.Mannose.toSVGNode().setPosition(new Point2D.Float(50, 37));
825         svg.addSubtag(man6);
826         backbone.addLine(man3.getCenterPoint(), man6.getCenterPoint());
827
828         if (9 == mannoseCount)
829         {
830            SvgNode man7 = Monosaccharide.Mannose.toSVGNode().setPosition(new Point2D.Float(30, 2));
831            svg.addSubtag(man7);
832            backbone.addLine(man4.getCenterPoint(), man7.getCenterPoint());
833
834            SvgNode man8 = Monosaccharide.Mannose.toSVGNode().setPosition(new Point2D.Float(30, 19));
835            svg.addSubtag(man8);
836            backbone.addLine(man5.getCenterPoint(), man8.getCenterPoint());
837
838            SvgNode man9 = Monosaccharide.Mannose.toSVGNode().setPosition(new Point2D.Float(30, 37));
839            svg.addSubtag(man9);
840            backbone.addLine(man6.getCenterPoint(), man9.getCenterPoint());
841         }
842         else if (mannoseCount > 6)
843         {
844            Rectangle2D man4bbox = man4.getBoundsBox();
845            Rectangle2D man6bbox = man6.getBoundsBox();
846
847            SvgPath curlyBrace = SvgPath.generateCurlyBracket(new Point2D.Double(man4bbox.getX() - 4, man4bbox.getY()),
848                                                              new Point2D.Double(man6bbox.getX() - 4, man6bbox.getY() + man6bbox.getHeight()), 10);
849            svg.addSubtag(curlyBrace);
850
851            Rectangle2D curlyBraceBBox = curlyBrace.getBoundsBox();
852
853            if (7 == mannoseCount)
854            {
855               svg.addSubtag(Monosaccharide.Mannose.toSVGNode().setPosition(new Point2D.Double(curlyBraceBBox.getX() - 14, (curlyBraceBBox.getY() + curlyBraceBBox.getHeight() / 2) - 5)));
856            }
857            else if (8 == mannoseCount)
858            {
859               svg.addSubtag(Monosaccharide.Mannose.toSVGNode().setPosition(new Point2D.Double(curlyBraceBBox.getX() - 14, (curlyBraceBBox.getY() + curlyBraceBBox.getHeight() * 0.25) - 5)));
860
861               svg.addSubtag(Monosaccharide.Mannose.toSVGNode().setPosition(new Point2D.Double(curlyBraceBBox.getX() - 14, (curlyBraceBBox.getY() + curlyBraceBBox.getHeight() * 0.75) - 5)));
862            }
863         }
864      }
865
866      return svg;
867   }
868
869
870   //--------------------------------------------------------------------------
871   public Glycan add(Monosaccharide inMonosaccharide, int inCount)
872   {
873      Integer currentCount = mMonosaccharideCountMap.get(inMonosaccharide);
874      int newCount = (currentCount != null ? currentCount : 0) + inCount;
875
876      int newBondCount = inCount - 1 + (mMonosaccharideCountMap.size() > 0 ? 1 : 0);
877
878      mMonosaccharideCountMap.put(inMonosaccharide, newCount);
879
880      super.add(inMonosaccharide, inCount);
881
882      super.remove(Molecule.H2O, newBondCount);
883
884      return this;
885   }
886
887
888   //--------------------------------------------------------------------------
889   /**
890    Returns a List of IonizableGroup objects.
891    @return List of IonizableGroups
892    */
893   public List<IonizableGroup> getKas()
894   {
895      List<IonizableGroup> ionizableGroups = null;
896
897      for (Monosaccharide monosaccharide : mMonosaccharideCountMap.keySet())
898      {
899         List<IonizableGroup> monosaccharideKas = monosaccharide.getKas();
900         if (CollectionUtil.hasValues(monosaccharideKas))
901         {
902            if (null == ionizableGroups)
903            {
904               ionizableGroups = new ArrayList<>(5);
905            }
906
907            for (IonizableGroup ionizableGroup : monosaccharideKas)
908            {
909               for (int i = 0; i < mMonosaccharideCountMap.get(monosaccharide); i++)
910               {
911                  ionizableGroups.add(ionizableGroup);
912               }
913            }
914         }
915      }
916
917      return ionizableGroups;
918   }
919
920}