content: /*************************************************************************
  *                                                                        *
  *  This file is part of the 20n/act project.                             *
  *  20n/act enables DNA prediction for synthetic biology/bioengineering.  *
  *  Copyright (C) 2017 20n Labs, Inc.                                     *
  *                                                                        *
  *  Please direct all queries to act@20n.com.                             *
  *                                                                        *
  *  This program is free software: you can redistribute it and/or modify  *
  *  it under the terms of the GNU General Public License as published by  *
  *  the Free Software Foundation, either version 3 of the License, or     *
  *  (at your option) any later version.                                   *
  *                                                                        *
  *  This program is distributed in the hope that it will be useful,       *
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of        *
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
  *  GNU General Public License for more details.                          *
  *                                                                        *
  *  You should have received a copy of the GNU General Public License     *
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>. *
  *                                                                        *
  *************************************************************************/
  
  package act.shared;
  
  import act.shared.helpers.MongoDBToJSON;
  import com.mongodb.DBObject;
  import org.json.JSONArray;
  import org.json.JSONException;
  import org.json.JSONObject;
  
  import java.io.Serializable;
  import java.util.Arrays;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Set;
  
  public class Seq implements Serializable {
    public enum AccDB { genbank, uniprot, swissprot, trembl, embl, ncbi_protein, metacyc, brenda };
    public enum AccType {genbank_nucleotide, genbank_protein, uniprot};
    private static final long serialVersionUID = 42L;
    Seq() { /* default constructor for serialization */ }
  
    private int id;
    private AccDB srcdb;
    private String sequence;
    private String ecnum;
    private String organism;
  
    private Long organismIDs;
    private List<JSONObject> references;
    private JSONObject metadata;
  
    private Set<Long> reactionsCatalyzed;
  
    private String geneName;
    private Set<String> uniprotAccs;
    private Set<String> genbankProtAccs;
    private Set<String> genbankNucAccs;
    private Set<String> brendaIDs;
    private Set<String> synonyms;
    private Set<String> productNames;
    private String catalyticActivity;
  
    public Seq(long id, String e, Long oid, String o, String s, List<JSONObject> r, DBObject m, AccDB d) {
      this.id = (new Long(id)).intValue();
      this.sequence = s;
      this.ecnum = e;
      this.organism = o;
      this.organismIDs = oid;
      this.references = r;
      this.srcdb = d;
      this.metadata = MongoDBToJSON.conv(m); // to JSONObject
  
      // extracted data from metadata:
      this.geneName        = meta(this.metadata, new String[] { "name" });
      if (this.metadata.has("xref")) {
        JSONObject xrefObject = metadata.getJSONObject("xref");
  
        if (xrefObject.has("brenda_id")) {
          this.brendaIDs = parseJSONArray(xrefObject.getJSONArray("brenda_id"));
        }
      }
      if (this.metadata.has("accession")) {
        // accounts for new structure of accessions in seq collection
        JSONObject accessions = metadata.getJSONObject("accession");
  
        if (accessions.has("uniprot")) {
          this.uniprotAccs = parseJSONArray(accessions.getJSONArray("uniprot"));
        }
  
        if (accessions.has("genbank_protein")) {
          this.genbankProtAccs = parseJSONArray(accessions.getJSONArray("genbank_protein"));
        }
  
        if (accessions.has("genbank_nucleotide")) {
          this.genbankNucAccs = parseJSONArray(accessions.getJSONArray("genbank_nucleotide"));
        }
      }
      if (this.metadata.has("product_names"))
        this.productNames = parseJSONArray((JSONArray) this.metadata.get("product_names"));
      if (this.metadata.has("synonyms"))
        this.synonyms = parseJSONArray((JSONArray) this.metadata.get("synonyms"));
      if (this.metadata.has("catalytic_activity"))
        this.catalyticActivity = this.metadata.getString("catalytic_activity");
  
      this.reactionsCatalyzed = new HashSet<Long>();
    }
  
    public static Seq rawInit(
      // the first set of arguments are the same as the constructor
      long id, String e, Long oid, String o, String s, List<JSONObject> r, DBObject m, AccDB d,
      // the next set of arguments are the ones that are typically "constructed"
      // but here, passed as raw input, e.g., when reading directly from db
      Set<Long> rxns
    ) {
      Seq seq = new Seq(id, e, oid, o, s, r, m, d);
  
      // overwrite from the raw data provided "as-is"
      seq.reactionsCatalyzed = rxns;
  
      return seq;
    }
  
    static final String notFound = "";
    private String meta(JSONObject o, String[] xpath) {
      Set<String> ret = meta(o, xpath, true);
      if (ret.size() > 0) {
        return ret.iterator().next();
      } else {
        return notFound;
      }
    }
  
    private Set<String> meta(JSONObject o, String[] xpath, boolean returnSet) {
      int len = xpath.length;
      Set<String> notFnd = new HashSet<String>();
      notFnd.add(notFound);
      try {
        if (len == 0)
          return notFnd;
  
        if (len == 1) {
          Object val = o.get(xpath[0]); // can throw
          Set<String> fnd = new HashSet<String>();
          if (val instanceof String) {
            fnd.add(o.getString(xpath[0])); // can throw
          } else if (val instanceof JSONArray) {
            JSONArray aval = (JSONArray)val;
            for (int i = 0; i<aval.length(); i++) fnd.add(aval.getString(i)); // can throw
          }
          return fnd;
        } else {
          String[] rest = Arrays.copyOfRange(xpath, 1, xpath.length);
          meta(o.getJSONObject(xpath[0]), rest, returnSet);  // can throw
        }
      } catch (JSONException ex) {
        return notFnd;
      }
      return notFnd;
    }
  
    private String meta(JSONObject o, String[] xpath, String field, String shouldEqual, String extractField) {
      int len = xpath.length;
      try {
        if (len == 0)
          return notFound;
  
        if (len == 1) {
          JSONArray a = o.getJSONArray(xpath[0]); // can throw
          // iterate over all objects
          for (int i = 0; i < a.length(); i++) {
            JSONObject oo = a.getJSONObject(i); // can throw
            // if the object has the "field" and its value equals "shouldEqual"
            if (shouldEqual.equals(oo.getString(field))) { // can throw
              // return on first match
              return oo.getString(extractField);
            }
          }
        } else {
          String[] rest = Arrays.copyOfRange(xpath, 1, xpath.length);
          meta(o.getJSONObject(xpath[0]), rest, field, shouldEqual, extractField);  // can throw
        }
      } catch (JSONException ex) {
        return notFound;
      }
      return notFound;
    }
  
    private Set<String> parseJSONArray(JSONArray jArray) {
      Set<String> listdata = new HashSet<>();
      if (jArray != null) {
        for (int i = 0; i < jArray.length(); i++) {
          listdata.add(jArray.get(i).toString());
        }
      }
      return listdata;
    }
  
    public int getUUID() { return this.id; }
    public String getSequence() { return this.sequence; }
    public String getEc() { return this.ecnum; }
    public String getOrgName() { return this.organism; }
    public Long getOrgId() { return this.organismIDs; }
    public List<JSONObject> getReferences() { return this.references; }
    public JSONObject getMetadata() { return this.metadata; }
    public Set<String> getProductNames() { return this.productNames; }
    public Set<String> getSynonyms() { return this.synonyms; }
    public String getCatalyticActivity() {return this.catalyticActivity; }
    public String getGeneName() { return this.geneName; }
    public Set<String> getUniprotAccession() { return this.uniprotAccs; }
    public Set<String> getGenbankProteinAccession() { return this.genbankProtAccs; }
    public Set<String> getGenbankNucleotideAccession() { return this.genbankNucAccs; }
    public Set<String> getBrendaIDs() { return this.brendaIDs; }
    public AccDB getSrcdb() { return this.srcdb; }
  
    public void addReactionsCatalyzed(Long r) { this.reactionsCatalyzed.add(r); }
    public Set<Long> getReactionsCatalyzed() { return this.reactionsCatalyzed; }
  
    public void setMetadata(JSONObject metadata) { this.metadata = metadata; }
    public void setReferences(List<JSONObject> refs) { this.references = refs; }
    public void setReactionsCatalyzed(Set<Long> reactionsCatalyzed) { this.reactionsCatalyzed = reactionsCatalyzed; }
    public void setOrgName(String orgName) { this.organism = orgName; }
    public void setOrgId(Long orgId) { this.organismIDs = orgId; }
  }
  
  
  