/**
   * Add a new qualifier to a list of qualifiers
   *
   * @param qualifiers
   * @param newQualifier
   */
  private void addNewQualifier(QualifierVector qualifiers, Qualifier newQualifier) {
    Qualifier qualifier;
    if ((qualifier = qualifiers.getQualifierByName(newQualifier.getName())) != null) {
      final StringVector newValues = newQualifier.getValues();
      final StringVector values = qualifier.getValues();

      if (newValues == null) return;
      for (int j = 0; j < newValues.size(); j++) {
        String newValue = (String) newValues.get(j);
        if (!values.contains(newValue)) qualifier.addValue(newValue);
      }
    } else qualifiers.addElement(newQualifier);
  }
 /**
  * Routine to combine transcript qualifiers and for multiple transcripts create links to the other
  * transcripts (other_transcript) and to use the transcript ID.
  *
  * @param qualifiers
  * @param transcript
  * @param ntranscripts
  * @param chadoGene
  */
 private int handleTranscripts(
     QualifierVector qualifiers,
     Feature transcript,
     int ntranscripts,
     ChadoCanonicalGene chadoGene) {
   QualifierVector transcriptQualifiers = transcript.getQualifiers().copy();
   combineQualifiers(qualifiers, transcriptQualifiers, false);
   ntranscripts = chadoGene.getTranscripts().size();
   if (ntranscripts > 1) {
     addNewQualifier(qualifiers, transcriptQualifiers.getQualifierByName("ID"));
     List<Feature> transcripts = chadoGene.getTranscripts();
     for (int i = 0; i < ntranscripts; i++) {
       Feature thisTranscript = (Feature) transcripts.get(i);
       String thisTranscriptName = GeneUtils.getUniqueName(thisTranscript);
       if (!thisTranscriptName.equals(GeneUtils.getUniqueName(transcript))) {
         Qualifier qualifier = new Qualifier("other_transcript", thisTranscriptName);
         addNewQualifier(qualifiers, qualifier);
       }
     }
   }
   return ntranscripts;
 }
  /**
   * Map GFF features to EMBL/Genbank
   *
   * @param feature
   * @return
   */
  private Object mapGffToNativeFeature(final Feature feature) {
    if (DATABASE_MAP_KEYS == null) initDatabaseMappings();

    Key key = feature.getKey();
    QualifierVector qualifiers = feature.getQualifiers().copy();

    // ignore if obsolete
    if (IGNORE_OBSOLETE_FEATURES) {
      Qualifier isObsoleteQualifier = qualifiers.getQualifierByName("isObsolete");
      if (isObsoleteQualifier != null) {
        String value = (String) isObsoleteQualifier.getValues().get(0);
        if (Boolean.parseBoolean(value)) return null;
      }
    }

    key = map(key, qualifiers);
    if (getEntryInformation().isValidQualifier((String) DATABASE_QUALIFIERS_TO_REMOVE[0])) {
      try {
        if (this instanceof EmblDocumentEntry)
          return new EmblStreamFeature(key, feature.getLocation(), qualifiers);
        else return new GenbankStreamFeature(key, feature.getLocation(), qualifiers);
      } catch (InvalidRelationException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
    }

    Location location = joinUtrs(feature, key, qualifiers);
    if (location == null) return null;
    // flatten gene model - combining qualifiers
    if (key.getKeyString().equals(DatabaseDocument.EXONMODEL)) {
      ChadoCanonicalGene chadoGene = ((GFFStreamFeature) feature).getChadoGene();

      final String name = GeneUtils.getUniqueName(feature);
      final String transcriptName = chadoGene.getTranscriptFromName(name);

      StringVector sv = new StringVector();
      sv.add(transcriptName);
      final Feature transcript = chadoGene.containsTranscript(sv);

      if (transcript != null && GeneUtils.isNonCodingTranscripts(transcript.getKey())) return null;

      qualifiers.removeQualifierByName("ID");
      int ntranscripts = 0;
      // add transcript & protein qualifiers to CDS
      try {
        final Feature protein = chadoGene.getProteinOfTranscript(transcriptName);
        if (protein != null) combineQualifiers(qualifiers, protein.getQualifiers().copy(), false);

        if (transcript != null)
          ntranscripts = handleTranscripts(qualifiers, transcript, ntranscripts, chadoGene);
      } catch (NullPointerException npe) {
      }

      // add gene qualifiers to CDS
      QualifierVector geneQualifiers = chadoGene.getGene().getQualifiers().copy();

      // multiple transcripts
      if (ntranscripts > 1 && geneQualifiers.getQualifierByName("ID") != null) {
        Qualifier newIDQualifier =
            new Qualifier(
                "shared_id", (String) geneQualifiers.getQualifierByName("ID").getValues().get(0));
        addNewQualifier(qualifiers, newIDQualifier);
        geneQualifiers.removeQualifierByName("ID");
      }
      combineQualifiers(qualifiers, geneQualifiers, true);
    } else if (GeneUtils.isNonCodingTranscripts(key)) {
      // use gene id for non-coding transcripts
      ChadoCanonicalGene chadoGene = ((GFFStreamFeature) feature).getChadoGene();
      if (chadoGene != null) {
        qualifiers.removeQualifierByName("ID");
        QualifierVector geneQualifiers = chadoGene.getGene().getQualifiers().copy();
        combineQualifiers(qualifiers, geneQualifiers, true);
      }
    }

    try {
      for (int i = 0; i < DATABASE_QUALIFIERS_TO_MAP.length; i++) {
        if (!getEntryInformation().isValidQualifier(DATABASE_QUALIFIERS_TO_MAP[i][0])) {
          changeQualifierName(
              qualifiers, DATABASE_QUALIFIERS_TO_MAP[i][0], DATABASE_QUALIFIERS_TO_MAP[i][1]);
        }
      }

      if (qualifiers.getQualifierByName("stop_codon_redefined_as_selenocysteine") != null) {
        handleSelenocysteine(qualifiers, feature);
      }

      for (int i = 0; i < DATABASE_QUALIFIERS_TO_REMOVE.length; i++) {
        if (!getEntryInformation().isValidQualifier((String) DATABASE_QUALIFIERS_TO_REMOVE[i]))
          qualifiers.removeQualifierByName((String) DATABASE_QUALIFIERS_TO_REMOVE[i]);
      }

      if (key.getKeyString().equals("polypeptide")) return null;
      else if (key.getKeyString().equals("gene")) return null;
      else if (key.getKeyString().equals("centromere")) return null;
      else if (key.getKeyString().equals("transcript") || key.getKeyString().equals("mRNA"))
        return null;

      if (this instanceof EmblDocumentEntry)
        return new EmblStreamFeature(key, location, qualifiers);
      else return new GenbankStreamFeature(key, location, qualifiers);
    } catch (InvalidRelationException e) {
      e.printStackTrace();
      if (feature instanceof DatabaseStreamFeature) return new EmblStreamFeature();
      else return new GenbankStreamFeature();
    }
  }