public void handleEndElement(
     OtterContentHandler theContentHandler,
     String namespaceURI,
     String localName,
     String qualifiedName) {
   Transcript trans = (Transcript) theContentHandler.getStackObject();
   trans.addProperty(TagHandler.AUTHOR_EMAIL, getCharacters());
   super.handleEndElement(theContentHandler, namespaceURI, localName, qualifiedName);
 }
  public Color getBackgroundBoxColor() {
    if (pos == targetNucleotide && tier == targetTier) return targetColor;
    int lowSeqSelect;
    int highSeqSelect;
    if (baseEditor instanceof BaseEditorPanel) {
      lowSeqSelect = ((BaseEditorPanel) baseEditor).selectLowPos();
      highSeqSelect = ((BaseEditorPanel) baseEditor).selectHighPos();
    } else {
      lowSeqSelect = -1;
      highSeqSelect = -1;
    }

    if (lowSeqSelect != -1 && highSeqSelect != -1 && pos >= lowSeqSelect && pos <= highSeqSelect) {
      return seqSelectColor;
    }

    int basePos = baseEditor.posToBasePair(pos);

    if (((BaseEditorPanel) baseEditor).isSequencingErrorPosition(basePos)) return shiftColor;

    if (isExon) {
      ExonI exon = null;
      if (currentFeature instanceof ExonI) exon = (ExonI) currentFeature;
      else if (currentFeature instanceof DrawableSeqFeature)
        exon = (ExonI) ((DrawableSeqFeature) currentFeature).getFeature();
      if (exon != null && exon.getTranscript() != null) {
        Transcript transcript = exon.getTranscript();
        int tss = (int) exon.getTranscript().getTranslationStart();
        if (basePos == tss
            || basePos == (tss + exon.getStrand())
            || basePos == (tss + (2 * exon.getStrand())))
          return DrawableUtil.getStartCodonColor(transcript);
        else {
          int tes = (int) transcript.getTranslationEnd();
          if (basePos == tes
              || basePos == (tes + exon.getStrand())
              || basePos == (tes + (2 * exon.getStrand()))) return Color.red;
          else {
            int shift_pos = (int) transcript.plus1FrameShiftPosition();
            if (basePos == shift_pos) return shiftColor;
            else {
              shift_pos = (int) transcript.minus1FrameShiftPosition();
              if (basePos == shift_pos) return shiftColor;
              else {
                int stop_pos = (int) transcript.readThroughStopPosition();
                if (basePos == stop_pos
                    || basePos == (stop_pos + exon.getStrand())
                    || basePos == (stop_pos + (2 * exon.getStrand()))) return Color.pink;
              }
            }
          }
        }
      }
      return transcriptColorList[transcriptColorIndex][exonColorIndex];
    }
    return null;
  }
 public void handleEndElement(
     OtterContentHandler theContentHandler,
     String namespaceURI,
     String localName,
     String qualifiedName) {
   Transcript transcript = (Transcript) theContentHandler.getStackObject();
   DbXref dbx = new DbXref("OtterId", getCharacters(), "otter");
   transcript.addDbXref(dbx);
   super.handleEndElement(theContentHandler, namespaceURI, localName, qualifiedName);
 }
  /*  Start AnnotationChangeListenter interface */
  public boolean handleAnnotationChangeEvent(AnnotationChangeEvent evt) {
    if (this.isVisible() && evt.getSource() != this) {
      if (evt.isCompound()) {
        for (int i = 0; i < evt.getNumberOfChildren(); ++i) {
          handleAnnotationChangeEvent(evt.getChildChangeEvent(i));
        }
        reformatAnnotationPanel();
        return true;
      }

      // can probably be done better, this will reformat the panel a lot
      reformatAnnotationPanel();

      if (evt.isEndOfEditSession()) {

        getOverview().updateState();
        setSelection(getSelection());
        updateEditRegion();

        repaint();
        updateTitle();
      }

      if (evt.isAdd()) {
        AnnotatedFeatureI annotToDisplay = null;
        AnnotatedFeatureI gene = evt.getChangedAnnot();
        int trans_count = gene.size();
        for (int i = 0; i < trans_count; ++i) {
          Transcript t = (Transcript) gene.getFeatureAt(i);
          if (evt.isUndo()) {
            // System.out.printf("Attaching %s(%d)\n", t, t.hashCode());
            // addFeatureToPanel(t, getAnnotationPanel());
            if (getSelection() != null && t.getName().equals(getSelection().getName())) {
              annotToDisplay = t;
            }
          }
        }

        if (annotToDisplay == null) {
          annotToDisplay = (AnnotatedFeatureI) evt.getChangedAnnot().getFeatureAt(0);
        } else {
          setSelection(annotToDisplay);
        }

        getOverview().updateState();
      }

      if (evt.isDelete()) {}
    }

    repaint();
    return true;
  }
  /**
   * Sets the selection the the feature given
   *
   * @param gai the new selection <br>
   *     side effects: update the overview, and update the title
   */
  public void setSelection(AnnotatedFeatureI gai) {
    if (gai != null && getTransOrOneLevelAnn(gai) != null) {
      this.selectedAnnotation = getTransOrOneLevelAnn(gai); // gai;
    }

    if (getSelection().isTranscript()) {
      Transcript transcript = (Transcript) getTransOrOneLevelAnn(gai);
      // do we really want to do this every time an annotation is selected?
      transcript.setTranslationEndFromStart();
      String translation = transcript.translate();
      SequenceI sequence = transcript.getPeptideSequence();
      getOverview().setTranscript(transcript);
    }
    this.getOverview().repaint();
    this.updateTitle();
  }
  public void handleEndElement(
      OtterContentHandler theContentHandler,
      String namespaceURI,
      String localName,
      String qualifiedName) {
    Transcript transcript = (Transcript) theContentHandler.getStackObject();
    Comment comment =
        new Comment(
            transcript.getId(),
            getCharacters(),
            "no author",
            0 // should be a long representing a timestamp.
            );
    transcript.addComment(comment);

    super.handleEndElement(theContentHandler, namespaceURI, localName, qualifiedName);
  }
  /** Sets the title for the window */
  public void updateTitle() {
    // todo - translationViewer take in 1 level annot
    if (getSelection() != null && getTransOrOneLevelAnn(getSelection()).isTranscript()) {
      Transcript transcript = (Transcript) getTransOrOneLevelAnn(getSelection());
      JFrame frame = (JFrame) this.getTopLevelAncestor();
      String title = "";
      if (getAnnotationPanel().getStrand() == Strand.FORWARD) {
        title += " + ";
      } else {
        title += " - ";
      }

      title += transcript.getName();

      if (getResultPanel().getStrand() == Strand.FORWARD) {
        title += " + ";
      } else {
        title += " - ";
      }

      frame.setTitle(title);
    }
  }
  /**
   * @param position
   * @param tier
   * @return
   */
  public static int getOffset(int position, TierI tier) {

    SeqFeatureI sf = null;

    int offset = 0;
    if (tier.featureExsitsAt(position, Level.BOTTOM)) {
      sf = tier.featureAt(position, Level.BOTTOM);
      ReadingFrame exonFrame = ReadingFrame.valueOf(sf.getFrame());
      ReadingFrame tierFrame = tier.getReadingFrame();

      if (exonFrame == ReadingFrame.NONE
          && sf.getParent() != null
          && sf.getParent().isTranscript()) {
        FeatureSetI parent = sf.getParent();
        Transcript parentClone = (Transcript) sf.getParent().clone();

        int start = parentClone.getEnd();

        // the transcript start might be different than the lowest exon start
        // when in the middle of an edit
        if (parentClone.canHaveChildren()) {
          for (int i = 0; i < parentClone.size(); i++) {
            SeqFeatureI f = parentClone.getFeatureAt(i);
            if ((Strand.valueOf(f.getStrand()) == Strand.FORWARD && f.getStart() < start)
                || (Strand.valueOf(f.getStrand()) == Strand.REVERSE && f.getStart() > start))
              start = f.getStart();
          }
        }

        int translationStart = parentClone.getTranslationStart();
        int translationPos = parentClone.getFeaturePosition(translationStart);
        int oldstart = start;
        start += ((translationPos - 1) % 3) * parentClone.getStrand();

        parentClone.setTranslationStart(start);
        parentClone.setTranslationEnd(parentClone.getEnd());
        parentClone.setPeptideSequence(null);
        String translation = parentClone.translate();
        SequenceI sequence = parentClone.getPeptideSequence();

        int index = parent.getFeatureIndex(sf);
        exonFrame = ReadingFrame.valueOf(parentClone.getFeatureAt(index).getFrame());
      }

      if (tierFrame != exonFrame && sf.getParent() != null && sf.getParent().isTranscript()) {
        if (tierFrame == ReadingFrame.THREE) {
          offset = exonFrame == ReadingFrame.ONE ? -2 : -1;
        } else if (tierFrame == ReadingFrame.TWO) {
          offset = exonFrame == ReadingFrame.ONE ? -1 : 1;
        } else if (tierFrame == ReadingFrame.ONE) {
          offset = exonFrame == ReadingFrame.TWO ? 1 : 2;
        }
      }

      if (exonFrame == ReadingFrame.NONE) {
        offset = 0;
      }
    }

    return offset;
  }
  /**
   * Get component to be rendered, if pos outside of current range getFeatureAtPosition and reset
   * currentRange, if feature is non null and not an instance of FeatureSetI then its an exon, and
   * set isExon flag
   */
  public Component getBaseComponent(int position, TierI tier, Orientation o) {

    transcript = null;

    int bp = tier.getBasePair(position);
    int oldPos = position;

    // We need to change the position if dealing with aa view
    if (tier.getType() == SequenceType.AA) {
      int offset = getOffset(position, tier);
      bp += offset * tier.getStrand().toInt();
      position = tier.getPosition(bp);
    }

    char base = tier.charAt(position);

    if (tier.featureExsitsAt(position, TierI.Level.BOTTOM)) {
      this.state = State.EXON;
      SeqFeatureI feature = tier.featureAt(position, TierI.Level.BOTTOM);
      Transcript parentClone = null;
      Transcript otherParentClone = null;

      if (tier.getType() == SequenceType.AA
          && feature.getParent() != null
          && feature.getParent().isTranscript()) {

        FeatureSetI parent = feature.getParent();
        int index = parent.getFeatureIndex(feature);

        int start = parent.getEnd();
        // the transcript start might be different than the lowest exon start
        // when in the middle of an edit
        if (parent.canHaveChildren()) {
          for (int i = 0; i < parent.size(); i++) {
            SeqFeatureI sf = parent.getFeatureAt(i);
            if ((Strand.valueOf(sf.getStrand()) == Strand.FORWARD && sf.getStart() < start)
                || (Strand.valueOf(sf.getStrand()) == Strand.REVERSE && sf.getStart() > start))
              start = sf.getStart();
          }
        }

        int translationStart = parent.getTranslationStart();
        int translationEnd = parent.getTranslationEnd();
        int translationPos = parent.getFeaturePosition(translationStart);
        int oldstart = start;
        start += ((translationPos - 1) % 3) * parent.getStrand();

        // Tring to avoid all the recalculation...
        if (feature.getParent() == currentParent
            && start == currentStart
            && featureStart == feature.getStart()
            && featureEnd == feature.getEnd()) {
          parentClone = currentParentClone;
          otherParentClone = currentParentClone2;
        } else {
          // need to get a full translation so that we can get the amino acids
          // in the UTR region
          parentClone = (Transcript) feature.getParent().clone();
          otherParentClone = (Transcript) feature.getParent().clone();

          if (!parentClone.setTranslationStart(start)) {
            start = oldstart;
            TranslationI cds = otherParentClone.getTranslation();
            cds.calcTranslationStartForLongestPeptide();
            translationStart = otherParentClone.getTranslationStart();
            translationPos = otherParentClone.getFeaturePosition(translationStart);
            start += ((translationPos - 1) % 3) * otherParentClone.getStrand();
            parentClone.setTranslationStart(start);
          }

          parentClone.setTranslationEnd(parentClone.getEnd());
          parentClone.setPeptideSequence(null);

          String translation = parentClone.translate();
          SequenceI sequence = parentClone.getPeptideSequence();

          currentParent = (Transcript) feature.getParent();
          currentParentClone = parentClone;
          currentParentClone2 = otherParentClone;
          currentStart = start;
          featureStart = feature.getStart();
          featureEnd = feature.getEnd();

          if (otherParentClone.getFeatureContaining(translationEnd) == null) {
            TranslationI cds = otherParentClone.getTranslation();
            cds.setTranslationEndFromStart();
          }
        }

        SeqFeatureI sf = parentClone.getFeatureAt(index);

        if (bp >= sf.getLow() && bp <= sf.getHigh()) {
          base = getPeptide(bp, sf);
        } else {
          base = '\0';
        }

        if (otherParentClone != null) {
          feature = otherParentClone.getFeatureAt(index);
        }

      } else if (tier.getType() == SequenceType.AA
          && feature.getParent() != null
          && !feature.getParent().isTranscript()) {
        base = '\0';
      }

      if (isUTR(bp, feature, tier)) {
        this.state = State.UTR;
      }

      if (isTranslationStart(bp, feature)) {
        this.state = State.TRANSLATION_START;
      }

      if (isTranslationEnd(bp, feature)) {
        this.state = State.TRANSLATION_END;
      }

      // Can have an error or a shift on a position but not both
      if (isSequencingErrorPosition(bp, feature, tier.getType())) {
        this.state = State.ERROR;
        // set base to the base of the new sequence?
      }

      if (isShiftPosition(bp, feature, tier.getType())) {
        this.state = State.SHIFT;
      }

    } else if (tier.featureExsitsAt(position, TierI.Level.TOP)) {
      SeqFeatureI feature = tier.featureAt(position, Level.TOP);
      this.state = State.INTRON;
      if (tier.getType() == SequenceType.AA) {
        base = '\0';
      }

      int start = feature.getEnd();
      int end = feature.getStart();

      if (feature.canHaveChildren()) {
        for (int i = 0; i < feature.size(); i++) {
          SeqFeatureI sf = feature.getFeatureAt(i);
          if ((Strand.valueOf(sf.getStrand()) == Strand.FORWARD && sf.getStart() < start)
              || (Strand.valueOf(sf.getStrand()) == Strand.REVERSE && sf.getStart() > start))
            start = sf.getStart();

          if ((Strand.valueOf(sf.getStrand()) == Strand.FORWARD && sf.getEnd() > end)
              || (Strand.valueOf(sf.getStrand()) == Strand.REVERSE && sf.getEnd() < end))
            end = sf.getEnd();
        }
      }

      if (bp <= Math.min(start, end) || bp >= Math.max(start, end)) {
        this.state = State.OUT;
        base = '\0';
      }
    } else {
      this.state = State.OUT;
      base = '\0';
    }

    if (!(state == State.SHIFT || state == State.ERROR)
        && getRegionLow() <= position
        && position <= getRegionHigh()) {
      this.state = State.SELECTED;
      base = tier.charAt(oldPos);
    }

    init(base);
    return this;
  }
  /**
   * Display AnnotatedFeatureI feature. Exon, Transcript, and Gene are all GenericAnnotationI. No
   * selection event is fired. (selectAnnot fires and displays)
   */
  private void displayAnnot(AnnotatedFeatureI annot) {
    currentAnnot = annot;
    if (currentAnnot == null) {
      transcriptComboBox.removeAllItems();
      transcriptComboBox.addItem("<no feature selected>");
      lengthLabel.setText("Translation length: <no feature selected>");
      upstream_button.setLabel("");
      downstream_button.setLabel("");
      translationViewer.setTranscript(null, editorPanel.getSelectedTier()); // ??
      return;
    }

    // else {
    setupTranscriptComboBox(currentAnnot);

    SeqFeatureI topAnnot = currentAnnot;
    if (topAnnot.isTranscript()) topAnnot = currentAnnot.getRefFeature();
    if (topAnnot.isProteinCodingGene()) {
      String translation = currentAnnot.translate();
      if (translation == null) {
        lengthLabel.setText("Translation length: <no start selected>");
      } else {
        lengthLabel.setText("Translation length: " + currentAnnot.translate().length());
      }
    } else {
      lengthLabel.setText(topAnnot.getFeatureType() + " annotation");
    }
    FeatureSetI holder = (FeatureSetI) topAnnot.getRefFeature();
    neighbor_up = null;
    neighbor_down = null;
    if (holder != null) {

      int index = holder.getFeatureIndex(topAnnot);
      // get next neighbor up that has whole sequence
      for (int i = index - 1; i >= 0 && neighbor_up == null; i--) {
        FeatureSetI gene_sib = (FeatureSetI) holder.getFeatureAt(i);
        if (gene_sib.getFeatureAt(0) instanceof Transcript) {
          Transcript trans = (Transcript) gene_sib.getFeatureAt(0);
          if (trans.haveWholeSequence()) // szap.getCurationSet()))
          neighbor_up = trans;
        }
      }

      // get next neighbor down that has whole sequence
      for (int i = index + 1; i < holder.size() && neighbor_down == null; i++) {
        FeatureSetI gene_sib = (FeatureSetI) holder.getFeatureAt(i);
        if (gene_sib.getFeatureAt(0) instanceof Transcript) {
          Transcript trans = (Transcript) gene_sib.getFeatureAt(0);
          if (trans.haveWholeSequence()) // szap.getCurationSet()))
          neighbor_down = trans;
        }
      }
    }
    upstream_button.setLabel(
        neighbor_up == null
            ? ""
            : "Go to next 5' annotation (" + neighbor_up.getParent().getName() + ")");
    upstream_button.setVisible(neighbor_up != null);
    downstream_button.setLabel(
        neighbor_down == null
            ? ""
            : "Go to next 3' annotation (" + neighbor_down.getParent().getName() + ")");
    downstream_button.setVisible(neighbor_down != null);
    // }
    // todo - translationViewer take in 1 level annot
    if (currentAnnot.isTranscript())
      translationViewer.setTranscript((Transcript) currentAnnot, editorPanel.getSelectedTier());
  }
  public boolean handleAnnotationChangeEvent(AnnotationChangeEvent evt) {
    if (this.isVisible() && evt.getSource() != this) {
      if (evt.isCompound()) {
        for (int i = 0; i < evt.getNumberOfChildren(); ++i) {
          handleAnnotationChangeEvent(evt.getChildChangeEvent(i));
        }
        return true;
      }

      if (evt.isEndOfEditSession()) {
        // Dont want scrolling on ann change, also sf for redraw is the
        // gene, so it just goes to 1st trans which is problematic.
        displayAnnot(currentAnnot);
        repaint(); // this is the needed repaint after the changes
        return true;
      }

      /*
      AnnotatedFeatureI gene = evt.getChangedAnnot();
      for (Object o : szap.getAnnotations().getFeatures()) {
        AnnotatedFeatureI feat = (AnnotatedFeatureI)o;
        if (feat.getId().equals(gene.getId())) {
          //gene = feat;
          break;
        }
      }
      */

      if (evt.isAdd()) {
        AnnotatedFeatureI annotToDisplay = null;
        AnnotatedFeatureI gene = evt.getChangedAnnot();
        int trans_count = gene.size();
        for (int i = 0; i < trans_count; ++i) {
          Transcript t = (Transcript) gene.getFeatureAt(i);
          if (evt.isUndo()) {
            // System.out.printf("Attaching %s(%d)\n", t, t.hashCode());
            editorPanel.attachAnnot(t);
            if (currentAnnot != null && t.getName().equals(currentAnnot.getName())) {
              annotToDisplay = t;
            }
          }
        }
        if (annotToDisplay == null) {
          annotToDisplay = (AnnotatedFeatureI) evt.getChangedAnnot().getFeatureAt(0);
        }
        displayAnnot(annotToDisplay);
        return true;
      }

      if (evt.isDelete()) {
        AnnotatedFeatureI gene = evt.getChangedAnnot();
        int trans_count = gene.size();
        for (int i = 0; i < trans_count; ++i) {
          if (evt.isUndo()) {
            Transcript t = (Transcript) gene.getFeatureAt(i);
            // System.out.printf("Detaching %s(%d)\n", t, t.hashCode());
            editorPanel.detachTranscript(t);
          }
        }
        return true;
      }

      /*
            // getAnnotation() can be null after a delete - return if null?
            SeqFeatureI sf = evt.getAnnotTop();

            // If the annotation event is not for the editors strand then return
            boolean is_reverse = (sf.getStrand() == -1);
            if (editorPanel.getReverseStrand() != is_reverse)
              return false;

            if (evt.isDelete() && evt.isRootAnnotChange()) {
              AnnotatedFeatureI gene = evt.getChangedAnnot();
              int trans_count = gene.size();
              for (int i = 0; i < trans_count; i++) {
                Transcript t = (Transcript) gene.getFeatureAt (i);
                editorPanel.detachTranscript (t);
                if (t == currentAnnot)
                  currentAnnot = null;
              }
            } else if (evt.isDelete() && evt.isTranscriptChange()) {
              Transcript t = (Transcript) evt.getChangedAnnot();
              editorPanel.detachTranscript (t);
              if (t == currentAnnot)
                currentAnnot = null;
            } else if ((evt.isAdd() || evt.isSplit() || evt.isMerge()) &&
                       evt.isRootAnnotChange()) {
              AnnotatedFeatureI gene = evt.getChangedAnnot();
              int trans_count = gene.size();
              for (int i = 0; i < trans_count; i++) {
                Transcript t = (Transcript) gene.getFeatureAt (i);
                int tier = editorPanel.attachAnnot (t);
              }
            } else if (evt.isAdd() && evt.isTranscriptChange()) {
              Transcript t = (Transcript) evt.getChangedAnnot();
              int tier = editorPanel.attachAnnot (t);
            }
            // Exon split or intron made - same thing isnt it?
            else if (evt.isAdd() && evt.isExonChange()) {
              Transcript t = (Transcript) evt.getParentFeature();
              // removing and adding transcript will force editorPanel
              // to pick up new exon
              editorPanel.detachTranscript(t);
              editorPanel.attachAnnot(t);
            }
            else if (evt.isDelete() && evt.isExonChange()) {
              Transcript t = (Transcript) evt.getParentFeature();
              // removing and adding transcript will force editorPanel to remove exon
              editorPanel.detachTranscript(t);
              editorPanel.attachAnnot(t);
              // or editorPanel.removeFeature((Exon)evt.getSecondFeature());
            }
      //       else if (evt.isReplace()) { //&&evt.isTopAnnotChange - always true of replace
      //         AnnotatedFeatureI old_gene = evt.getReplacedFeature();
      //         AnnotatedFeatureI new_gene = (AnnotatedFeatureI)evt.getChangedFeature();
      //         int trans_count = old_gene.size();
      //         /* This does assume that there are no changes in the number
      //            of transcripts between the old and the new. Might be safer
      //            to separate the 2 loops */
      //         for (int i = 0; i < trans_count; i++) {
      //           Transcript t;
      //           t = (Transcript) old_gene.getFeatureAt (i);
      //           editorPanel.detachTranscript (t);
      //           t = (Transcript) new_gene.getFeatureAt (i);
      //           int tier = editorPanel.attachTranscript (t);
      //         }
      //         // This is so that we don't select a different transcript
      //         AnnotatedFeatureI current_gene = (current_transcript != null ?
      //                                           current_transcript.getGene() : null);
      //         if (current_gene != null && current_gene == old_gene) {
      //           int index = current_gene.getFeatureIndex(current_transcript);
      //           current_transcript = (Transcript) new_gene.getFeatureAt(index);
      //         }
      //       }
      // this isnt possible - all replaces are for top annots
      //       else if (evt.isReplace() && evt.isTranscriptChange()) {
      //         Transcript old_trans = (Transcript)evt.getReplacedFeature();
      //         Transcript new_trans = (Transcript)evt.getChangedFeature();
      //         editorPanel.detachTranscript (old_trans);
      //         int tier = editorPanel.attachTranscript (new_trans);
      //         // This is so that we don't select a different transcript
      //         if (current_transcript != null && current_transcript == old_trans) {
      //           current_transcript = new_trans;
      //         }
      //       }
    }

    return true;
  }