FattyAcid.java

  1. /*
  2.  * Copyright 2021 Dominik Kopczynski, Nils Hoffmann.
  3.  *
  4.  * Licensed under the Apache License, Version 2.0 (the "License");
  5.  * you may not use this file except in compliance with the License.
  6.  * You may obtain a copy of the License at
  7.  *
  8.  *      http://www.apache.org/licenses/LICENSE-2.0
  9.  *
  10.  * Unless required by applicable law or agreed to in writing, software
  11.  * distributed under the License is distributed on an "AS IS" BASIS,
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13.  * See the License for the specific language governing permissions and
  14.  * limitations under the License.
  15.  */
  16. package org.lifstools.jgoslin.domain;

  17. import com.fasterxml.jackson.annotation.JsonIgnore;
  18. import java.util.ArrayList;
  19. import java.util.Arrays;
  20. import java.util.Collections;
  21. import java.util.HashSet;
  22. import java.util.HashMap;
  23. import java.util.Map.Entry;
  24. import java.util.Set;

  25. /**
  26.  * A fatty acid with a specific type. This object defines the name, position,
  27.  * number of carbon atoms, hydroxyls and double bonds, as well as the bond type
  28.  * to the head group. A FattyAcid can carry optional modifications / functional groups and can
  29.  * report double bond positions.
  30.  *
  31.  * @author Dominik Kopczynski
  32.  * @author Nils Hoffmann
  33.  */
  34. public class FattyAcid extends FunctionalGroup {

  35.     protected int numCarbon;
  36.     protected LipidFaBondType lipidFaBondType;
  37.     private final Set<String> fgExceptions = new HashSet<>(Arrays.asList("acyl", "alkyl", "cy", "cc", "acetoxy"));

  38.     public FattyAcid(String _name, KnownFunctionalGroups knownFunctionalGroups) {
  39.         this(_name, 0, null, null, LipidFaBondType.ESTER, 0, knownFunctionalGroups);
  40.     }

  41.     public FattyAcid(String _name, int _num_carbon, DoubleBonds _double_bonds) {
  42.         this(_name, _num_carbon, _double_bonds, null, LipidFaBondType.ESTER, 0, null);
  43.     }

  44.     public FattyAcid(String _name, int _num_carbon, DoubleBonds _double_bonds, KnownFunctionalGroups knownFunctionalGroups) {
  45.         this(_name, _num_carbon, _double_bonds, null, LipidFaBondType.ESTER, 0, knownFunctionalGroups);
  46.     }

  47.     public FattyAcid(String _name, int _num_carbon, KnownFunctionalGroups knownFunctionalGroups) {
  48.         this(_name, _num_carbon, null, null, LipidFaBondType.ESTER, 0, knownFunctionalGroups);
  49.     }

  50.     public FattyAcid(String _name, int _num_carbon, DoubleBonds _double_bonds, HashMap<String, ArrayList<FunctionalGroup>> _functional_groups, KnownFunctionalGroups knownFunctionalGroups) {
  51.         this(_name, _num_carbon, _double_bonds, _functional_groups, LipidFaBondType.ESTER, 0, knownFunctionalGroups);
  52.     }

  53.     public FattyAcid(String _name, int _num_carbon, DoubleBonds _double_bonds, HashMap<String, ArrayList<FunctionalGroup>> _functional_groups, LipidFaBondType _lipid_FA_bond_type, KnownFunctionalGroups knownFunctionalGroups) {
  54.         this(_name, _num_carbon, _double_bonds, _functional_groups, _lipid_FA_bond_type, 0, knownFunctionalGroups);
  55.     }

  56.     public FattyAcid(String _name, int _num_carbon, DoubleBonds _double_bonds, HashMap<String, ArrayList<FunctionalGroup>> _functional_groups, LipidFaBondType _lipid_FA_bond_type, int _position, KnownFunctionalGroups knownFunctionalGroups) {
  57.         super(_name, _position, 1, _double_bonds, false, "", null, _functional_groups, knownFunctionalGroups);
  58.         numCarbon = _num_carbon;
  59.         lipidFaBondType = _lipid_FA_bond_type;

  60.         if (lipidFaBondType == LipidFaBondType.LCB_REGULAR) {
  61.             functionalGroups.put("[X]", new ArrayList<>());
  62.             functionalGroups.get("[X]").add(knownFunctionalGroups.get("X"));
  63.         }

  64.         if (numCarbon < 0 || numCarbon == 1) {
  65.             throw new ConstraintViolationException("FattyAcid must have at least 2 carbons! Got " + Integer.toString(numCarbon));
  66.         }

  67.         if (getPosition() < 0) {
  68.             throw new ConstraintViolationException("FattyAcid position must be greater or equal to 0! Got " + Integer.toString(getPosition()));
  69.         }

  70.         if (doubleBonds.getNumDoubleBonds() < 0) {
  71.             throw new ConstraintViolationException("FattyAcid must have at least 0 double bonds! Got " + Integer.toString(doubleBonds.getNumDoubleBonds()));
  72.         }
  73.     }

  74.     @Override
  75.     public FunctionalGroup copy() {
  76.         DoubleBonds db = doubleBonds.copy();
  77.         HashMap<String, ArrayList<FunctionalGroup>> fg = new HashMap<>();
  78.         for (Entry<String, ArrayList<FunctionalGroup>> kv : functionalGroups.entrySet()) {
  79.             fg.put(kv.getKey(), new ArrayList<>());
  80.             for (FunctionalGroup func_group : kv.getValue()) {
  81.                 fg.get(kv.getKey()).add(func_group.copy());
  82.             }
  83.         }

  84.         return new FattyAcid(getName(), numCarbon, db, fg, lipidFaBondType, getPosition(), knownFunctionalGroups);
  85.     }

  86.     public void setType(LipidFaBondType _lipid_FA_bond_type) {
  87.         lipidFaBondType = _lipid_FA_bond_type;
  88.         if (lipidFaBondType == LipidFaBondType.LCB_REGULAR && !functionalGroups.containsKey("[X]")) {
  89.             functionalGroups.put("[X]", new ArrayList<>());
  90.             functionalGroups.get("[X]").add(knownFunctionalGroups.get("X"));
  91.         } else if (functionalGroups.containsKey("[X]")) {
  92.             functionalGroups.remove("[X]");
  93.         }

  94.         setName((lipidFaBondType != LipidFaBondType.LCB_EXCEPTION && lipidFaBondType != LipidFaBondType.LCB_REGULAR) ? "FA" : "LCB");
  95.     }

  96.     public String getPrefix(LipidFaBondType lipid_FA_bond_type) {
  97.         switch (lipid_FA_bond_type) {
  98.             case ETHER_PLASMANYL:
  99.                 return "O-";
  100.             case ETHER_PLASMENYL:
  101.                 return "P-";
  102.             default:
  103.                 return "";
  104.         }
  105.     }

  106.     @Override
  107.     public int getNDoubleBonds() {
  108.         return super.getNDoubleBonds() + ((lipidFaBondType == LipidFaBondType.ETHER_PLASMENYL) ? 1 : 0);
  109.     }

  110.     public boolean lipidFaBondTypePrefix(LipidFaBondType lipid_FA_bond_type) {
  111.         return (lipid_FA_bond_type == LipidFaBondType.ETHER_PLASMANYL) || (lipid_FA_bond_type == LipidFaBondType.ETHER_PLASMENYL) || (lipid_FA_bond_type == LipidFaBondType.ETHER_UNSPECIFIED);
  112.     }

  113.     @Override
  114.     public String toString(LipidLevel level) {
  115.         StringBuilder fa_string = new StringBuilder();
  116.         fa_string.append(getPrefix(lipidFaBondType));
  117.         int num_carbons = numCarbon;
  118.         int num_double_bonds = doubleBonds.getNumDoubleBonds();

  119.         if (num_carbons == 0 && num_double_bonds == 0 && !LipidLevel.isLevel(level, LipidLevel.COMPLETE_STRUCTURE.level | LipidLevel.FULL_STRUCTURE.level | LipidLevel.STRUCTURE_DEFINED.level | LipidLevel.SN_POSITION.level)) {
  120.             return "";
  121.         }

  122.         if (LipidLevel.isLevel(level, LipidLevel.SN_POSITION.level | LipidLevel.MOLECULAR_SPECIES.level)) {
  123.             ElementTable e = computeAndCopyElements();
  124.             num_carbons = e.get(Element.C);
  125.             num_double_bonds = getNDoubleBonds() - ((lipidFaBondType == LipidFaBondType.ETHER_PLASMENYL) ? 1 : 0);
  126.         }

  127.         fa_string.append(num_carbons).append(":").append(num_double_bonds);

  128.         if (!LipidLevel.isLevel(level, LipidLevel.SN_POSITION.level | LipidLevel.MOLECULAR_SPECIES.level) && doubleBonds.doubleBondPositions.size() > 0) {
  129.             fa_string.append("(");

  130.             int i = 0;
  131.             ArrayList<Integer> sorted_db = new ArrayList<>(doubleBonds.doubleBondPositions.keySet());
  132.             Collections.sort(sorted_db, (a, b) -> (int) a - (int) b);
  133.             for (int db_pos : sorted_db) {
  134.                 if (i++ > 0) {
  135.                     fa_string.append(",");
  136.                 }
  137.                 fa_string.append(db_pos);
  138.                 if (LipidLevel.isLevel(level, LipidLevel.COMPLETE_STRUCTURE.level | LipidLevel.FULL_STRUCTURE.level)) {
  139.                     fa_string.append(doubleBonds.doubleBondPositions.get(db_pos));
  140.                 }
  141.             }
  142.             fa_string.append(")");
  143.         }
  144.        
  145.        
  146.         if (level == LipidLevel.COMPLETE_STRUCTURE && stereochemistry.length() > 0){
  147.             fa_string.append("[").append(stereochemistry).append("]");
  148.         }
  149.        

  150.         if (LipidLevel.isLevel(level, LipidLevel.COMPLETE_STRUCTURE.level | LipidLevel.FULL_STRUCTURE.level)) {
  151.             ArrayList<String> fg_names = new ArrayList<>();
  152.             for (Entry<String, ArrayList<FunctionalGroup>> kv : functionalGroups.entrySet()) {
  153.                 fg_names.add(kv.getKey());
  154.             }
  155.             Collections.sort(fg_names, (String a, String b) -> a.toLowerCase().compareTo(b.toLowerCase()));

  156.             for (String fg : fg_names) {
  157.                 if (fg.equals("[X]")) {
  158.                     continue;
  159.                 }
  160.                 ArrayList<FunctionalGroup> fg_list = functionalGroups.get(fg);
  161.                 if (fg_list.isEmpty()) {
  162.                     continue;
  163.                 }

  164.                 Collections.sort(fg_list, (FunctionalGroup a, FunctionalGroup b) -> a.getPosition() - b.getPosition());

  165.                 int i = 0;
  166.                 fa_string.append(";");
  167.                 for (FunctionalGroup func_group : fg_list) {
  168.                     if (i++ > 0) {
  169.                         fa_string.append(",");
  170.                     }
  171.                     fa_string.append(func_group.toString(level));
  172.                 }
  173.             }
  174.         } else if (level == LipidLevel.STRUCTURE_DEFINED) {
  175.             ArrayList<String> fg_names = new ArrayList<>();
  176.             functionalGroups.entrySet().forEach(kv -> {
  177.                 fg_names.add(kv.getKey());
  178.             });
  179.             Collections.sort(fg_names, (String a, String b) -> a.toLowerCase().compareTo(b.toLowerCase()));

  180.             for (String fg : fg_names) {
  181.                 if (fg.equals("[X]")) {
  182.                     continue;
  183.                 }
  184.                 ArrayList<FunctionalGroup> fg_list = functionalGroups.get(fg);
  185.                 if (fg_list.isEmpty()) {
  186.                     continue;
  187.                 }

  188.                 if (fgExceptions.contains(fg)) {
  189.                     fa_string.append(";");
  190.                     int i = 0;
  191.                     for (FunctionalGroup func_group : fg_list) {
  192.                         if (i++ > 0) {
  193.                             fa_string.append(",");
  194.                         }
  195.                         fa_string.append(func_group.toString(level));
  196.                     }
  197.                 } else {
  198.                     int fg_count = 0;
  199.                     for (FunctionalGroup func_group : fg_list) {
  200.                         fg_count += func_group.getCount();
  201.                     }

  202.                     if (fg_count > 1) {
  203.                         fa_string.append(";").append(!fg_list.get(0).atomic ? ("(" + fg + ")" + Integer.toString(fg_count)) : (fg + Integer.toString(fg_count)));
  204.                     } else {
  205.                         fa_string.append(";").append(fg);
  206.                     }
  207.                 }
  208.             }

  209.         } else {
  210.             ElementTable func_elements = getFunctionalGroupElements();
  211.             for (int i = 2; i < Elements.ELEMENT_ORDER.size(); ++i) {
  212.                 Element e = Elements.ELEMENT_ORDER.get(i);
  213.                 if (func_elements.get(e) > 0) {
  214.                     fa_string.append(";").append(Elements.ELEMENT_SHORTCUT.get(e));
  215.                     if (func_elements.get(e) > 1) {
  216.                         fa_string.append(func_elements.get(e));
  217.                     }
  218.                 }
  219.             }
  220.         }

  221.         return fa_string.toString();
  222.     }

  223.     @JsonIgnore
  224.     @Override
  225.     public ElementTable getFunctionalGroupElements() {
  226.         ElementTable fgElements = super.getFunctionalGroupElements();

  227.         // subtract the invisible [X] functional group for regular LCBs
  228.         if (lipidFaBondType == LipidFaBondType.LCB_REGULAR && functionalGroups.containsKey("O")) {
  229.             fgElements.put(Element.O, fgElements.get(Element.O) - 1);
  230.         }

  231.         return fgElements;
  232.     }

  233.     @Override
  234.     public void computeElements() {
  235.         elements = new ElementTable();

  236.         int num_double_bonds = doubleBonds.getNumDoubleBonds();
  237.         if (lipidFaBondType == LipidFaBondType.ETHER_PLASMENYL) {
  238.             num_double_bonds += 1;
  239.         }

  240.         if (numCarbon == 0 && num_double_bonds == 0) {
  241.             elements.put(Element.H, 1);
  242.             return;
  243.         }

  244.         if (lipidFaBondType != LipidFaBondType.LCB_EXCEPTION && lipidFaBondType != LipidFaBondType.LCB_REGULAR) {
  245.             elements.put(Element.C, numCarbon); // carbon
  246.             switch (lipidFaBondType) {
  247.                 case ESTER:
  248.                     elements.put(Element.H, (2 * numCarbon - 1 - 2 * num_double_bonds)); // hydrogen
  249.                     elements.put(Element.O, 1); // oxygen
  250.                     break;
  251.                 case ETHER_PLASMENYL:
  252.                     elements.put(Element.H, (2 * numCarbon - 1 - 2 * num_double_bonds + 2)); // hydrogen
  253.                     break;
  254.                 case ETHER, ETHER_PLASMANYL:
  255.                     elements.put(Element.H, ((numCarbon + 1) * 2 - 1 - 2 * num_double_bonds)); // hydrogen
  256.                     break;
  257.                 case AMIDE:
  258.                     elements.put(Element.H, (2 * numCarbon + 1 - 2 * num_double_bonds) - 1); // hydrogen
  259.                     break;
  260.                 default:
  261.                     throw new LipidException("Mass cannot be computed for fatty acyl chain with this bond type");
  262.             }
  263.         } else {
  264.             // long chain base
  265.             elements.put(Element.C, numCarbon); // carbon
  266.             elements.put(Element.H, (2 * (numCarbon - num_double_bonds) + 1)); // hydrogen
  267.             elements.put(Element.N, 1); // nitrogen
  268.         }
  269.     }

  270.     public int getNumCarbon() {
  271.         return numCarbon;
  272.     }

  273.     public void setNumCarbon(int numCarbon) {
  274.         this.numCarbon = numCarbon;
  275.     }

  276.     public LipidFaBondType getLipidFaBondType() {
  277.         return lipidFaBondType;
  278.     }

  279.     public void setLipidFaBondType(LipidFaBondType lipidFaBondType) {
  280.         this.lipidFaBondType = lipidFaBondType;
  281.     }
  282.    
  283. }