uk.ac.ebi.intact.editor.controller.curate.AnnotatedObjectController.java Source code

Java tutorial

Introduction

Here is the source code for uk.ac.ebi.intact.editor.controller.curate.AnnotatedObjectController.java

Source

/**
 * Copyright 2010 The European Bioinformatics Institute, and others.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package uk.ac.ebi.intact.editor.controller.curate;

import org.apache.commons.lang.StringUtils;
import org.primefaces.event.TabChangeEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import psidev.psi.mi.jami.bridges.exception.BridgeFailedException;
import psidev.psi.mi.jami.bridges.fetcher.OntologyTermFetcher;
import psidev.psi.mi.jami.bridges.ols.CachedOlsOntologyTermFetcher;
import psidev.psi.mi.jami.model.*;
import psidev.psi.mi.jami.utils.AliasUtils;
import psidev.psi.mi.jami.utils.AnnotationUtils;
import psidev.psi.mi.jami.utils.XrefUtils;
import uk.ac.ebi.intact.editor.controller.BaseController;
import uk.ac.ebi.intact.editor.controller.curate.cloner.EditorCloner;
import uk.ac.ebi.intact.editor.controller.curate.complex.ComplexController;
import uk.ac.ebi.intact.editor.controller.curate.publication.PublicationController;
import uk.ac.ebi.intact.editor.services.curate.EditorObjectService;
import uk.ac.ebi.intact.editor.services.curate.cvobject.CvObjectService;
import uk.ac.ebi.intact.editor.services.curate.experiment.ExperimentEditorService;
import uk.ac.ebi.intact.editor.services.curate.feature.FeatureEditorService;
import uk.ac.ebi.intact.editor.services.curate.institution.InstitutionService;
import uk.ac.ebi.intact.editor.services.curate.interaction.InteractionEditorService;
import uk.ac.ebi.intact.editor.services.curate.interactor.InteractorEditorService;
import uk.ac.ebi.intact.editor.services.curate.participant.ParticipantEditorService;
import uk.ac.ebi.intact.editor.services.curate.publication.PublicationEditorService;
import uk.ac.ebi.intact.jami.ApplicationContextProvider;
import uk.ac.ebi.intact.jami.model.IntactPrimaryObject;
import uk.ac.ebi.intact.jami.model.audit.Auditable;
import uk.ac.ebi.intact.jami.model.extension.*;
import uk.ac.ebi.intact.jami.model.lifecycle.LifeCycleEvent;
import uk.ac.ebi.intact.jami.model.lifecycle.LifeCycleEventType;
import uk.ac.ebi.intact.jami.model.lifecycle.Releasable;
import uk.ac.ebi.intact.jami.synchronizer.IntactDbSynchronizer;
import uk.ac.ebi.intact.jami.utils.IntactUtils;
import uk.ac.ebi.pride.utilities.ols.web.service.client.OLSClient;
import uk.ac.ebi.pride.utilities.ols.web.service.config.OLSWsConfigProd;
import uk.ac.ebi.pride.utilities.ols.web.service.model.Identifier;

import javax.annotation.Resource;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.faces.event.AjaxBehaviorEvent;
import javax.faces.event.ValueChangeEvent;
import javax.faces.validator.ValidatorException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author Bruno Aranda (baranda@ebi.ac.uk)
 * @version $Id$
 */
public abstract class AnnotatedObjectController extends BaseController implements ValueChangeAware {

    private final Logger log = LoggerFactory.getLogger(AnnotatedObjectController.class);

    private Date lastSaved;

    @Autowired
    private CuratorContextController curatorContextController;

    @Autowired
    private CurateController curateController;

    @Autowired
    private ChangesController changesController;

    @Resource(name = "editorObjectService")
    private transient EditorObjectService editorService;

    @Resource(name = "cvObjectService")
    private transient CvObjectService cvService;

    private boolean isAnnotationTopicDisabled;
    private boolean isXrefDisabled;
    private boolean isAliasDisabled;

    public static final String PROCESS = "process";
    public static final String PROCESS_MI_REF = "MI:0359";
    public static final String COMPONENT = "component";
    public static final String COMPONENT_MI_REF = "MI:0354";
    public static final String FUNCTION = "function";
    public static final String FUNCTION_MI_REF = "MI:0355";
    public static final String NON_UNIPROT = "no-uniprot-update";

    private transient OntologyTermFetcher goServerProxy;

    private String cautionMessage;
    private String internalRemark;
    private String description;

    private CvTerm newDatabase;
    private String newXrefId;
    private String newSecondaryId;
    private String newXrefVersion;
    private CvTerm newQualifier;

    private CvTerm newAliasType;
    private String newAliasName;

    private CvTerm newTopic;
    private String newAnnotationDescription;

    public AnnotatedObjectController() {
    }

    public abstract IntactPrimaryObject getAnnotatedObject();

    public abstract void setAnnotatedObject(IntactPrimaryObject annotatedObject);

    public void refreshTabsAndFocusXref() {
        isXrefDisabled = false;
        isAliasDisabled = true;
        isAnnotationTopicDisabled = true;
    }

    public void refreshTabs() {
        isXrefDisabled = true;
        isAliasDisabled = true;
        isAnnotationTopicDisabled = true;
    }

    public String goToParent() {
        AnnotatedObjectController parentController = getParentController();
        if (parentController == null || parentController.getAnnotatedObject() == null) {
            return "/curate/curate?faces-redirect=true";
        }

        return "/curate/" + getParentController().getPageContext() + "?faces-redirect=true&includeViewParams=true";
    }

    protected abstract AnnotatedObjectController getParentController();

    protected abstract String getPageContext();

    protected void generalLoadChecks() {
        if (getAnnotatedObject() != null) {
            // set current user
            getEditorService().setUser(getCurrentUser());

            if (changesController.isObjectBeingEdited(getAnnotatedObject(), false)) {
                String who = changesController.whoIsEditingObject(getAnnotatedObject());

                addWarningMessage("This object is already being edited by: " + who,
                        "Modifications may be lost or affect current work by the other curator");
            }

            loadCautionMessages();
        }
    }

    protected abstract void loadCautionMessages();

    protected void generalComplexLoadChecks() {

        ComplexController complexController = (ComplexController) getSpringContext().getBean("complexController");

        if (complexController.getComplex() != null) {
            IntactComplex complex = complexController.getComplex();

            switch (complex.getStatus()) {
            case CURATION_IN_PROGRESS:
                if (!getUserSessionController().isItMe(complex.getCurrentOwner())
                        && complex.getCurrentOwner() != null) {
                    addWarningMessage("Complex being curated by '" + complex.getCurrentOwner().getLogin() + "'",
                            "Please do not modify it without permission");
                }
                break;
            case READY_FOR_CHECKING:
                if (!getUserSessionController().isItMe(complex.getCurrentReviewer())
                        && complex.getCurrentReviewer() != null) {
                    addWarningMessage("Complex under review",
                            "This complex is being reviewed by '" + complex.getCurrentReviewer().getLogin() + "'");
                }
                break;
            case ACCEPTED_ON_HOLD:
                addWarningMessage("Complex on-hold", "Reason: " + complex.getOnHoldComment());
                break;
            case RELEASED:
                SimpleDateFormat sdf = new SimpleDateFormat("d MMM yyyy");
                Date releasedDate = null;
                for (LifeCycleEvent evt : collectLifecycleEvents(complex)) {
                    if (LifeCycleEventType.RELEASED == evt.getEvent()) {
                        releasedDate = evt.getWhen();
                    }
                }
                addInfoMessage("Complex already released",
                        "This complex was released on " + (releasedDate != null ? sdf.format(releasedDate) : ""));
                break;
            case NEW:

                addWarningMessage("Complex with start status",
                        "Assuming that it has been imported. Assign it to yourself if you are happy with this assumption");
                break;
            default:
                break;
            }
        }
    }

    protected void generalPublicationLoadChecks() {

        PublicationController publicationController = (PublicationController) getSpringContext()
                .getBean("publicationController");

        if (publicationController.getPublication() != null) {
            IntactPublication publication = publicationController.getPublication();

            switch (publication.getStatus()) {
            case CURATION_IN_PROGRESS:
                if (!getUserSessionController().isItMe(publication.getCurrentOwner())
                        && publication.getCurrentOwner() != null) {
                    addWarningMessage(
                            "Publication being curated by '" + publication.getCurrentOwner().getLogin() + "'",
                            "Please do not modify it without permission");
                }
                break;
            case READY_FOR_CHECKING:
                if (!getUserSessionController().isItMe(publication.getCurrentReviewer())
                        && publication.getCurrentReviewer() != null) {
                    addWarningMessage("Publication under review", "This publication is being reviewed by '"
                            + publication.getCurrentReviewer().getLogin() + "'");
                }
                break;
            case ACCEPTED_ON_HOLD:
                addWarningMessage("Publication on-hold", "Reason: " + publication.getOnHoldComment());
                break;
            case RELEASED:
                SimpleDateFormat sdf = new SimpleDateFormat("d MMM yyyy");
                Date releasedDate = null;
                for (LifeCycleEvent evt : collectLifecycleEvents(publication)) {
                    if (LifeCycleEventType.RELEASED == evt.getEvent()) {
                        releasedDate = evt.getWhen();
                    }
                }
                addInfoMessage("Publication already released", "This publication was released on "
                        + (releasedDate != null ? sdf.format(releasedDate) : ""));
                break;
            case NEW:
                addWarningMessage("Publication with start status",
                        "Assuming that it has been imported. Assign it to yourself if you are happy with this assumption");
                break;
            default:
                break;
            }

        }
    }

    protected Collection<LifeCycleEvent> collectLifecycleEvents(Releasable releasable) {
        if (releasable.areLifeCycleEventsInitialized()) {
            return releasable.getLifecycleEvents();
        } else {
            // reload releasable without flushing changes
            return getEditorService().initialiseLifecycleEvents(releasable);
        }
    }

    public void unsavedValueChange(ValueChangeEvent evt) {
        if (evt.getOldValue() != null && !evt.getOldValue().equals(evt.getNewValue())) {
            setUnsavedChanges(true);
        } else if (evt.getNewValue() != null && !evt.getNewValue().equals(evt.getOldValue())) {
            setUnsavedChanges(true);
        }
    }

    public String doSave() {
        doSave(true);
        return null;
    }

    /**
     * Executes the deletions and save the current object using the <code>CorePersister</code>. It invokes preSave()
     * before saving just in case a specific controller needs to prepare the object for the save operation. After invoking
     * the CorePersister's save(), it invokes the doSaveDetails() callback that can be used to handle whatever is not handled
     * by the CorePersister (ie. wrapped components). At the end, the current object is refreshed from the database.
     *
     * @param evt the action faces event
     */
    public void doSave(ActionEvent evt) {
        // this method will save and refresh the current view
        doSave(true);
    }

    /**
     * Executes the deletions and save the current object using the <code>CorePersister</code>. It invokes preSave()
     * before saving just in case a specific controller needs to prepare the object for the save operation. After invoking
     * the CorePersister's save(), it invokes the doSaveDetails() callback that can be used to handle whatever is not handled
     * by the CorePersister (ie. wrapped components). At the end, the current object is refreshed from the database.
     */
    public void doSave(boolean refreshCurrentView) {
        if (getAnnotatedObject() != null) {
            doSaveIntact(refreshCurrentView, changesController);
        }
    }

    protected void doSaveIntact(boolean refreshCurrentView, ChangesController changesController) {

        String currentAc = getAnnotatedObject().getAc();
        boolean currentAnnotatedObjectDeleted = false;
        boolean bypassSavingMessageForRange = false;

        try {
            Collection<String> duplicatedAcs = getEditorService().findObjectDuplicates(getAnnotatedObject(),
                    getDbSynchronizer());
            if (!duplicatedAcs.isEmpty()) {
                addErrorMessage(
                        duplicatedAcs.size() + " identical object exists: " + StringUtils.join(duplicatedAcs, ", "),
                        "Cannot save identical objects");
                FacesContext.getCurrentInstance().renderResponse();
            } else {

                // annotated objects specific tasks to prepare the save/delete
                doPreSave();

                boolean saved = false;

                // delete from the unsaved manager
                currentAnnotatedObjectDeleted = processDeleteEvents(currentAc);
                if (currentAnnotatedObjectDeleted) {
                    saved = true;
                }

                // only save object if parent saved otherwise, call the save method on parent object
                IntactPrimaryObject annotatedObject = getAnnotatedObject();

                if (isParentObjectNotSaved()) {
                    AnnotatedObjectController parent = getParentController();
                    if (parent != null) {
                        parent.doSaveIntact(refreshCurrentView, changesController);
                        saved = true;
                    }
                } else {
                    if (!currentAnnotatedObjectDeleted) {
                        if (annotatedObject != null && checkIfRangeReq(annotatedObject)) {// this is done not to display save messages for range saving
                            bypassSavingMessageForRange = true;
                        }
                        annotatedObject = getEditorService().doSave(annotatedObject, getDbSynchronizer());

                        if (annotatedObject != null) {
                            saved = true;
                            // saves specific elements for each annotated object (e.g. components in interactions)
                            boolean detailsSaved = doSaveDetails();

                            if (detailsSaved)
                                saved = true;

                            lastSaved = new Date();
                            changesController.removeFromUnsaved(annotatedObject,
                                    collectParentAcsOfCurrentAnnotatedObject());
                        }
                    }
                }

                // we refresh the object if it has been saved
                if (annotatedObject != null) {
                    setAnnotatedObject(annotatedObject);
                    if (!bypassSavingMessageForRange) {
                        addInfoMessage("Saved", getAc() + ": " + getDescription());
                    }
                    doPostSave();
                }

                if (refreshCurrentView) {
                    refreshCurrentViewObject();
                }
            }
        } catch (Throwable t) {
            handleException(t);
        }
    }

    private boolean checkIfRangeReq(IntactPrimaryObject annotatedObject) {
        if (annotatedObject instanceof IntactFeatureEvidence) {
            IntactFeatureEvidence intactFeatureEvidence = (IntactFeatureEvidence) annotatedObject;
            Iterator<Range> iterator = intactFeatureEvidence.getRanges().iterator();
            while (iterator.hasNext()) {
                ExperimentalRange range = (ExperimentalRange) iterator.next();
                if (range.getAc() == null) {
                    return true;
                }
            }
        } else if (annotatedObject instanceof IntactModelledFeature) {
            IntactModelledFeature intactModelledFeature = (IntactModelledFeature) annotatedObject;
            Iterator<Range> iterator = intactModelledFeature.getRanges().iterator();
            while (iterator.hasNext()) {
                ModelledRange range = (ModelledRange) iterator.next();
                if (range.getAc() == null) {
                    return true;
                }
            }
        }

        return false;

    }

    protected boolean processDeleteEvents(String currentAc) {
        boolean delete = false;
        // delete from the unsaved manager
        final List<UnsavedChange> deletedObjects = new ArrayList(changesController.getAllUnsavedDeleted());

        EditorObjectService editorService = getEditorService();

        for (UnsavedChange unsaved : deletedObjects) {

            IntactPrimaryObject unsavedObject = unsaved.getUnsavedObject();

            // when an object is deleted, other deleted events can become obsolete and could have been removed from the deleted change events
            if (changesController.getAllUnsavedDeleted().contains(unsaved)) {
                // the object to delete is the current object itself. Should delete it now
                if (unsavedObject.getAc() != null && unsavedObject.getAc().equals(currentAc)) {

                    // remove the object to delete from its parent. If it is successful and the current object has been deleted, we can say that the save is successful
                    delete = editorService.doDelete(unsavedObject, unsaved.getDbSynchronizer());

                    postProcessDeletedEvent(unsaved);

                }
                // the object to delete is different from the current object. Checks that the scope of this object to delete is the ac of the current object being saved
                // if the scope is null or different, the object should not be deleted at this stage because we only save the current object and changes associated with it
                // if current ac is null, no deleted event should be associated with it as this object has not been saved yet
                else if (unsaved.getScope() != null && unsaved.getScope().equals(currentAc)) {
                    // remove the object to delete from its parent
                    delete = editorService.doDelete(unsavedObject, unsaved.getDbSynchronizer());

                    postProcessDeletedEvent(unsaved);
                }
            }

            if (delete) {
                changesController.removeFromDeleted(unsavedObject);
            }
        }

        return delete;
    }

    protected void postProcessDeletedEvent(UnsavedChange unsaved) {
        //nothing to do
    }

    private IntactPrimaryObject refresh(IntactPrimaryObject annotatedObject) {

        if (annotatedObject != null) {
            boolean isNew = false;

            if (getAnnotatedObject() != null) {
                isNew = (getAnnotatedObject().getAc() == null);
            }

            if (!isNew) {
                IntactPrimaryObject refreshed = getEditorService().refresh(annotatedObject);
                initialiseDefaultProperties(refreshed);
                return refreshed;
            }
        }

        return annotatedObject;
    }

    protected abstract void initialiseDefaultProperties(IntactPrimaryObject annotatedObject);

    protected void refreshCurrentViewObject() {
        if (curateController.getCurrentAnnotatedObjectController() != null) {
            final IntactPrimaryObject currentAo = curateController.getCurrentAnnotatedObjectController()
                    .getAnnotatedObject();

            if (currentAo != null && currentAo.getAc() != null) {
                refreshCurrentViewIntactObject(curateController, currentAo);
            }
        }
    }

    protected void refreshCurrentViewIntactObject(CurateController curateController,
            IntactPrimaryObject currentAo) {
        // we have to refresh because the current annotated object is different from the annotated object of this controller
        if (getAnnotatedObject() != null && !currentAo.getAc().equals(getAnnotatedObject().getAc())) {
            if (log.isDebugEnabled()) {
                log.debug("Refreshing object in view: " + getAnnotatedObject().toString());
            }
            IntactPrimaryObject refreshedAo = curateController.getCurrentAnnotatedObjectController()
                    .refresh(currentAo);
            curateController.getCurrentAnnotatedObjectController().setAnnotatedObject(refreshedAo);
        } else if (getAnnotatedObject() == null && currentAo != null) {
            if (log.isDebugEnabled()) {
                log.debug("Refreshing object in view: " + getAnnotatedObject().toString());
            }
            IntactPrimaryObject refreshedAo = refresh(currentAo);
            curateController.getCurrentAnnotatedObjectController().setAnnotatedObject(refreshedAo);
        }
    }

    public void forceRefreshCurrentViewObject() {

        final IntactPrimaryObject currentAo = curateController.getCurrentAnnotatedObjectController()
                .getAnnotatedObject();

        if (currentAo != null && currentAo.getAc() != null) {

            if (log.isDebugEnabled())
                log.debug("Refreshing object in view: " + currentAo.toString());

            IntactPrimaryObject refreshedAo = refresh(currentAo);
            curateController.getCurrentAnnotatedObjectController().setAnnotatedObject(refreshedAo);
        }
    }

    public void doSaveIfNecessary(ActionEvent evt) {
        if (getAnnotatedObject() != null && getAnnotatedObject().getAc() == null) {
            doSave(null);
        }
    }

    public void doPreSave() {
    }

    public void doPostSave() {
    }

    public boolean doSaveDetails() {
        return false;
    }

    public void validateAnnotatedObject(FacesContext context, UIComponent component, Object value)
            throws ValidatorException {

    }

    public void doRevertChanges(ActionEvent evt) {
        if (getAnnotatedObject() != null && getAnnotatedObject().getAc() == null) {
            doCancelEdition();
        } else {
            // revertJami first all unsaved events attached to any children of this object (will avoid to persist new annotations on children eg. copy publication annotations to experiments
            // could not be reverted otherwise)
            refreshUnsavedChangesBeforeRevert();

            EditorObjectService editorService = getEditorService();
            if (getAnnotatedObject() != null) {
                setAnnotatedObject(editorService.doRevert(getAnnotatedObject()));
            }

            postRevert();

            addInfoMessage("Changes reverted", "");
        }
    }

    protected void postRevert() {
        // nothing by default
    }

    public String doCancelEdition() {
        addInfoMessage("Canceled", "");

        refreshUnsavedChangesBeforeRevert();

        return goToParent();

    }

    protected void refreshUnsavedChangesBeforeRevert() {
        if (getAnnotatedObject() != null) {
            changesController.removeFromUnsaved(getAnnotatedObject(), Collections.EMPTY_LIST);
        }
    }

    public void changed() {
        setUnsavedChanges(true);
    }

    public void changed(ActionEvent evt) {
        setUnsavedChanges(true);
    }

    @Override
    public void changed(AjaxBehaviorEvent evt) {
        setUnsavedChanges(true);
    }

    public String clone() {
        if (getAnnotatedObject() != null) {
            return clone(getAnnotatedObject(), newClonerInstance());
        }
        return null;
    }

    protected String clone(IntactPrimaryObject ao, EditorCloner cloner) {
        IntactPrimaryObject clone = cloneAnnotatedObject(ao, cloner);

        if (clone == null)
            return null;

        addInfoMessage("Cloned annotated object", null);

        setAnnotatedObject(clone);

        setUnsavedChanges(true);

        return getCurateController().edit(clone);
    }

    protected <T extends IntactPrimaryObject> T cloneAnnotatedObject(T ao, EditorCloner cloner) {
        T clone = null;

        try {
            clone = getEditorService().cloneAnnotatedObject(ao, cloner);
        } catch (Exception e) {
            addErrorMessage("Could not clone object", e.getMessage());
            handleException(e);
            return null;
        }

        modifyClone(clone);

        return clone;
    }

    public void modifyClone(IntactPrimaryObject clone) {
        refreshTabsAndFocusXref();
    }

    protected abstract EditorCloner newClonerInstance();

    public String doDelete() {
        EditorObjectService editorObjectService = getEditorService();

        if (getAnnotatedObject() != null
                && editorObjectService.doDelete(getAnnotatedObject(), getDbSynchronizer())) {
            setAnnotatedObject(null);
            return goToParent();
        }
        // if delete not successfull, just display the message and don't go to the parent because the message will be lost
        // keep editing this object
        else {
            return curateController.edit(getAnnotatedObject());
        }
    }

    protected void doPostDelete() {
        // nothing to do by default
    }

    // XREFS
    ///////////////////////////////////////////////

    public void xrefChanged(AbstractIntactXref xref) {

        CvTerm goDb = null;

        if (xref.getId() != null && (xref.getId().startsWith("go:") || xref.getId().startsWith("GO:"))) {

            xref.setId(xref.getId().toUpperCase());

            if (xref.getAc() == null) {
                try {
                    OntologyTerm goTerm = getGoServerProxy().fetchByIdentifier(xref.getId(), Xref.GO.toLowerCase());

                    if (goTerm != null) {
                        if (goDb == null)
                            goDb = IntactUtils.createMIDatabase(psidev.psi.mi.jami.model.Xref.GO,
                                    psidev.psi.mi.jami.model.Xref.GO_MI);

                        xref.setDatabase(goDb);
                        xref.setSecondaryId(goTerm.getFullName());
                        //TODO Disables until we are able to do that with JAMI.
                        //                        Collection<OntologyTerm> parents = new ArrayList<OntologyTerm>(goTerm.getParents());
                        //                        // we have a root term
                        //                        if (parents.isEmpty()) {
                        //                            parents.add(goTerm);
                        //                        }

                        //                        CvTerm qualifier = calculateQualifier(parents);

                        CvTerm qualifier = null;
                        //Find the needed annotation. Using the ols_client directly.. Not good pratice, but otherwise
                        //we need to re-release everything again. Issue #14 psi-jami
                        OLSClient olsClient = new OLSClient(new OLSWsConfigProd());
                        Map<String, List<String>> annotations = olsClient
                                .getAnnotations(new Identifier(xref.getId(), Identifier.IdentifierType.OBO), "go");
                        String namespace = annotations.get("has_obo_namespace").get(0);

                        if ("biological_process".equals(namespace)) {
                            qualifier = IntactUtils.createMIQualifier(PROCESS, PROCESS_MI_REF);
                        } else if ("molecular_function".equals(namespace)) { // GO:0005554 was an alternative id for molecular function
                            qualifier = IntactUtils.createMIQualifier(FUNCTION, FUNCTION_MI_REF);
                        } else if ("cellular_component".equals(namespace)) {
                            qualifier = IntactUtils.createMIQualifier(COMPONENT, COMPONENT_MI_REF);
                        }

                        xref.setQualifier(qualifier);
                    }
                } catch (Throwable e) {
                    handleException(e);
                    return;
                }
            }
        }
    }

    //TODO Disables until we are able to do that with JAMI.
    //    private CvTerm calculateQualifier(Collection<OntologyTerm> parents) {
    //        if (parents.isEmpty()) return null;
    //
    //        Iterator<OntologyTerm> parentIterator = parents.iterator();
    //        OntologyTerm firstParent = parentIterator.next();
    //        while (firstParent.getIdentifiers().isEmpty() &&
    //                parentIterator.hasNext()){
    //            firstParent = parentIterator.next();
    //        }
    //        if (firstParent.getIdentifiers().isEmpty()){
    //            return null;
    //        }
    //        Collection<Annotation> x = firstParent.getAnnotations();
    //        String goId = firstParent.getIdentifiers().iterator().next().getId();
    //
    //        CvTerm terms = null;
    //
    //        if ("GO:0008150".equals(goId)) {
    //            terms = IntactUtils.createMIQualifier(PROCESS, PROCESS_MI_REF);
    //        } else if ("GO:0003674".equals(goId)) { // GO:0005554 was an alternative id for molecular function
    //            terms = IntactUtils.createMIQualifier(FUNCTION, FUNCTION_MI_REF);
    //        } else if ("GO:0005575".equals(goId)) {
    //            terms = IntactUtils.createMIQualifier(COMPONENT, COMPONENT_MI_REF);
    //        }
    //        if (terms == null){
    //            for (OntologyTerm parent : parents){
    //                Collection<OntologyTerm> parents2 = parent.getParents();
    //                CvTerm id = calculateQualifier(parents2);
    //                if (id != null){
    //                   return id;
    //                }
    //            }
    //            if (log.isWarnEnabled()) log.warn("No qualifier found for category: " + goId);
    //        }
    //
    //        return terms;
    //    }

    public void newXref(ActionEvent evt) {
        if (this.newDatabase != null && this.newXrefId != null) {
            AbstractIntactXref newRef = newXref(this.newDatabase, this.newXrefId, this.newSecondaryId,
                    this.newXrefVersion, this.newQualifier);
            // check if go
            xrefChanged(newRef);
            // add xref to object
            addNewXref(newRef);
            // save
            doSave(false);

            this.newDatabase = null;
            this.newXrefId = null;
            this.newXrefVersion = null;
            this.newQualifier = null;
            this.newSecondaryId = null;
        } else {
            addErrorMessage("Cannot add new xref as the database and/or primary identifier is(are) missing",
                    "No database/primary identifier provided");
        }
    }

    protected abstract void addNewXref(AbstractIntactXref newRef);

    protected abstract <T extends AbstractIntactXref> T newXref(CvTerm db, String id, String secondaryId,
            String version, CvTerm qualifier);

    protected abstract <T extends AbstractIntactXref> T newXref(String db, String dbMI, String id, String version,
            String qualifier, String qualifierMI);

    public abstract List<psidev.psi.mi.jami.model.Xref> collectXrefs();

    public void updateXref(String database, String databaseMI, String primaryId, String qualifier,
            String qualifierMI, Collection<Xref> refs) {
        if (database == null) {
            throw new IllegalArgumentException(
                    "Impossible to create/update/delete cross references if the database is not set.");
        }

        if (primaryId != null && !primaryId.isEmpty()) {
            replaceOrCreateXref(database, databaseMI, primaryId, null, qualifier, qualifierMI, refs);
        } else {
            removeXref(database, databaseMI, qualifier, qualifierMI, refs);
        }
    }

    public void replaceOrCreateXref(String database, String databaseMI, String primaryId, String version,
            String qualifier, String qualifierMI, Collection<Xref> refs) {
        if (database == null) {
            throw new IllegalArgumentException(
                    "Impossible to replace or create cross references if the database is not set.");
        }
        if (primaryId == null) {
            throw new IllegalArgumentException(
                    "Impossible to replace or create cross references if the primary id is not set.");
        }

        // modify if exists
        Collection<Xref> existingRefs = XrefUtils.collectAllXrefsHavingDatabaseAndQualifier(refs, databaseMI,
                database, qualifierMI, qualifier);
        Xref existingRef = !existingRefs.isEmpty() ? existingRefs.iterator().next() : null;
        // update if existing
        if (existingRef instanceof AbstractIntactXref) {
            AbstractIntactXref intactRef = (AbstractIntactXref) existingRef;
            intactRef.setVersion(version);
            intactRef.setId(primaryId);
        }
        // create if not exists
        else {
            refs.removeAll(existingRefs);
            refs.add(newXref(database, databaseMI, primaryId, version, qualifier, qualifierMI));
        }
        setUnsavedChanges(true);
    }

    protected void removeXref(String database, String databaseMI, String qualifier, String qualifierMI,
            Collection<Xref> refs) {
        if (database == null) {
            throw new IllegalArgumentException(
                    "Impossible to replace or create cross references if the database is not set.");
        }

        // modify if exists
        Collection<Xref> existingRefs = XrefUtils.collectAllXrefsHavingDatabaseAndQualifier(refs, databaseMI,
                database, qualifierMI, qualifier);
        refs.removeAll(existingRefs);
        setUnsavedChanges(true);
    }

    public abstract void removeXref(Xref xref);

    public void removeXref(Xref xref, Collection<Xref> refs) {
        Iterator<Xref> refIterator = refs.iterator();
        while (refIterator.hasNext()) {
            if (refIterator.next() == xref) {
                refIterator.remove();
            }
        }
        setUnsavedChanges(true);
    }

    public void addXref(Xref xref, Collection<Xref> refs) {
        addXref(xref.getDatabase(), xref.getId(), null, xref.getVersion(), xref.getQualifier(), refs);
    }

    public void addXref(CvTerm database, String primaryId, String secondaryId, String version, CvTerm qualifier,
            Collection<Xref> refs) {
        if (database == null) {
            throw new IllegalArgumentException("Impossible to add cross references if the database is not set.");
        }
        if (primaryId == null) {
            throw new IllegalArgumentException("Impossible to add cross references if the primary id is not set.");
        }

        refs.add(newXref(database, primaryId, secondaryId, version, qualifier));
        setUnsavedChanges(true);
    }

    public void addXref(String database, String databaseMI, String primaryId, String version, String qualifier,
            String qualifierMI, Collection<Xref> refs) {
        if (database == null) {
            throw new IllegalArgumentException("Impossible to add cross references if the database is not set.");
        }
        if (primaryId == null) {
            throw new IllegalArgumentException("Impossible to add cross references if the primary id is not set.");
        }

        refs.add(newXref(database, databaseMI, primaryId, version, qualifier, qualifierMI));
        setUnsavedChanges(true);
    }

    public boolean isXrefValid(Xref xref) {
        if (xref == null)
            return false;
        if (xref.getId() == null || xref.getId().equals("to set"))
            return false;
        if (xref.getDatabase() == null || xref.getDatabase().getShortName().equals("to set"))
            return false;

        CvTerm ao = xref.getDatabase();

        final Annotation annotation = AnnotationUtils.collectFirstAnnotationWithTopic(ao.getAnnotations(),
                Annotation.VALIDATION_REGEXP_MI, Annotation.VALIDATION_REGEXP);

        if (annotation == null)
            return true;
        else if (annotation.getValue() == null) {
            return false;
        }
        return xref.getId().matches(annotation.getValue());
    }

    public String externalLink(Xref xref) {
        if (xref == null)
            return null;
        if (xref.getId() == null || xref.getId().equals("to set"))
            return null;
        if (xref.getDatabase() == null || xref.getDatabase().getShortName().equals("to set"))
            return null;

        CvTerm ao = xref.getDatabase();

        final Annotation annotation = AnnotationUtils.collectFirstAnnotationWithTopic(ao.getAnnotations(),
                Annotation.SEARCH_URL_MI, Annotation.SEARCH_URL);
        if (annotation == null || annotation.getValue() == null)
            return null;

        String extUrl = annotation.getValue();
        return extUrl.replaceAll("\\$\\{ac\\}", xref.getId());
    }

    protected String navigateToObject(IntactPrimaryObject annotatedObject) {
        return curateController.newIntactObject(annotatedObject);
    }

    // ANNOTATIONS
    ///////////////////////////////////////////////

    public void newAnnotation(ActionEvent evt) {
        if (this.newTopic != null) {
            // new annot
            AbstractIntactAnnotation newAnnot = newAnnotation(this.newTopic, this.newAnnotationDescription);
            // add annot
            addNewAnnotation(newAnnot);
            // save changes
            doSave(false);

            this.newTopic = null;
            this.newAnnotationDescription = null;
        } else {
            addErrorMessage("Cannot add new annotation as it does not have any topics", "Missing annotation topic");
        }
    }

    protected abstract void addNewAnnotation(AbstractIntactAnnotation newAnnot);

    public abstract <T extends AbstractIntactAnnotation> T newAnnotation(CvTerm annotation, String text);

    public abstract <T extends AbstractIntactAnnotation> T newAnnotation(String topic, String topicMI, String text);

    public void addAnnotation(String topic, String topicMI, String text, Collection<Annotation> annots) {
        if (topic == null) {
            throw new IllegalArgumentException("The topic must be set before creating an annotation.");
        }

        Annotation annotation = newAnnotation(topic, topicMI, text);
        annots.add(annotation);
        setUnsavedChanges(true);
    }

    public void removeAnnotation(String topic, String topicMI, Collection<Annotation> annots) {
        if (topic == null) {
            throw new IllegalArgumentException(
                    "Impossible to replace or create annotations if the topic is not set.");
        }

        // modify if exists
        Collection<Annotation> existingAnnots = AnnotationUtils.collectAllAnnotationsHavingTopic(annots, topicMI,
                topic);
        annots.removeAll(existingAnnots);
        setUnsavedChanges(true);
    }

    public void removeAnnotation(String topic, String topicMI, String value, Collection<Annotation> annots) {
        if (topic == null) {
            throw new IllegalArgumentException(
                    "Impossible to replace or create annotations if the topic is not set.");
        }

        // modify if exists
        Collection<Annotation> existingAnnots = AnnotationUtils.collectAllAnnotationsHavingTopic(annots, topicMI,
                topic);
        for (Annotation ann : existingAnnots) {
            if (value == null && ann.getValue() == null) {
                annots.remove(ann);
            } else if (value != null && value.equals(ann.getValue())) {
                annots.remove(ann);
            }
        }
        setUnsavedChanges(true);
    }

    public abstract void removeAnnotation(Annotation annotation);

    public void removeAnnotation(Annotation annotation, Collection<Annotation> annots) {
        Iterator<Annotation> refIterator = annots.iterator();
        while (refIterator.hasNext()) {
            if (refIterator.next() == annotation) {
                refIterator.remove();
            }
        }
        setUnsavedChanges(true);
    }

    public void updateAnnotation(String topic, String topicMI, String value, Collection<Annotation> annots) {
        if (topic == null) {
            throw new IllegalArgumentException(
                    "Impossible to create/update/delete annotations if the topic is not set.");
        }

        replaceOrCreateAnnotation(topic, topicMI, value, annots);
    }

    public void replaceOrCreateAnnotation(String topic, String topicMI, String text,
            Collection<Annotation> annots) {
        if (topic == null) {
            throw new IllegalArgumentException("The topic must be set before creating or replacing an annotation.");
        }

        // modify if exists
        Collection<Annotation> existingAnnots = AnnotationUtils.collectAllAnnotationsHavingTopic(annots, topicMI,
                topic);
        Annotation existingAnnot = !existingAnnots.isEmpty() ? existingAnnots.iterator().next() : null;
        // update if existing
        if (existingAnnot != null) {
            existingAnnot.setValue(text);
        }
        // create if not exists
        else {
            annots.add(newAnnotation(topic, topicMI, text));
        }
        setUnsavedChanges(true);
    }

    public abstract List<Annotation> collectAnnotations();

    // ALIASES
    ///////////////////////////////////////////////

    public void newAlias(ActionEvent evt) {
        if (this.newAliasName != null && this.newAliasType != null) {
            // create new alias
            AbstractIntactAlias newAlias = newAlias(this.newAliasType, this.newAliasName);
            // add alias
            addNewAlias(newAlias);
            // save
            doSave(false);

            this.newAliasType = null;
            this.newAliasName = null;
        } else {
            addErrorMessage("Cannot add the new alias as it does not have a name and/or type",
                    "Alias without name and/or type");
        }
    }

    protected abstract void addNewAlias(AbstractIntactAlias newAlias);

    public abstract <T extends AbstractIntactAlias> T newAlias(CvTerm aliasType, String name);

    public abstract <T extends AbstractIntactAlias> T newAlias(String alias, String aliasMI, String name);

    public void addAlias(String alias, String aliasMI, String text, Collection<Alias> aliases) {
        if (text == null) {
            throw new IllegalArgumentException("The alias name must be set before creating an alias.");
        }

        Alias al = newAlias(alias, aliasMI, text);
        aliases.add(al);
        setUnsavedChanges(true);
    }

    public void setAlias(String alias, String aliasMI, String text, Collection<Alias> aliases) {

        if (text != null && !text.toString().isEmpty()) {
            replaceOrCreateAlias(alias, aliasMI, text, aliases);
        } else {
            removeAlias(alias, aliasMI, aliases);
        }
    }

    public void replaceOrCreateAlias(String alias, String aliasMI, String name, Collection<Alias> aliases) {
        if (name == null) {
            throw new IllegalArgumentException("Impossible to replace or create aliases if the name is not set.");
        }

        // modify if exists
        Collection<Alias> existingAliases = AliasUtils.collectAllAliasesHavingType(aliases, aliasMI, alias);
        Alias existingAlias = !existingAliases.isEmpty() ? existingAliases.iterator().next() : null;
        // update if existing
        if (existingAlias instanceof AbstractIntactAlias) {
            AbstractIntactAlias intactAlias = (AbstractIntactAlias) existingAlias;
            intactAlias.setName(name);
        }
        // create if not exists
        else {
            aliases.removeAll(existingAliases);
            aliases.add(newAlias(alias, aliasMI, name));
        }
        setUnsavedChanges(true);
    }

    public void removeAlias(String alias, String aliasMI, String text, Collection<Alias> aliases) {
        if (text == null) {
            throw new IllegalArgumentException("Impossible to replace or create aliases if the name is not set.");
        }

        // modify if exists
        Collection<Alias> existingAliases = AliasUtils.collectAllAliasesHavingTypeAndName(aliases, aliasMI, alias,
                text);
        aliases.removeAll(existingAliases);
        setUnsavedChanges(true);
    }

    public void removeAlias(String alias, String aliasMI, Collection<Alias> aliases) {

        // modify if exists
        Collection<Alias> existingAliases = AliasUtils.collectAllAliasesHavingType(aliases, aliasMI, alias);
        aliases.removeAll(existingAliases);
        setUnsavedChanges(true);
    }

    public abstract void removeAlias(Alias alias);

    public void removeAlias(Alias alias, Collection<Alias> aliases) {
        Iterator<Alias> refIterator = aliases.iterator();
        while (refIterator.hasNext()) {
            if (refIterator.next() == alias) {
                refIterator.remove();
            }
        }
        setUnsavedChanges(true);
    }

    public abstract List<Alias> collectAliases();

    // OTHER
    ////////////////////////////////////////////////////

    public String getCautionMessage() {
        return this.cautionMessage;
    }

    public String getCautionMessage(IntactPrimaryObject ao) {
        if (ao == null)
            return null;
        Collection<Annotation> annotations = Collections.EMPTY_LIST;
        if (ao instanceof IntactPublication) {
            IntactPublication publication = (IntactPublication) ao;
            if (publication.areAnnotationsInitialized()) {
                annotations = publication.getAnnotations();
            } else {
                annotations = ((PublicationEditorService) ApplicationContextProvider
                        .getBean("publicationEditorService")).initialisePublicationAnnotations(publication);
            }
        } else if (ao instanceof IntactExperiment) {
            IntactExperiment experiment = (IntactExperiment) ao;
            if (experiment.areAnnotationsInitialized()) {
                annotations = experiment.getAnnotations();
            } else {
                annotations = ((ExperimentEditorService) ApplicationContextProvider
                        .getBean("experimentEditorService")).initialiseExperimentAnnotations(experiment);
            }
        } else if (ao instanceof IntactInteractionEvidence) {
            IntactInteractionEvidence interaction = (IntactInteractionEvidence) ao;
            if (interaction.areAnnotationsInitialized()) {
                annotations = interaction.getAnnotations();
            } else {
                annotations = ((InteractionEditorService) ApplicationContextProvider
                        .getBean("interactionEditorService")).initialiseInteractionAnnotations(interaction);
            }
        } else if (ao instanceof IntactInteractor) {
            IntactInteractor interactor = (IntactInteractor) ao;
            if (interactor.areAnnotationsInitialized()) {
                annotations = interactor.getAnnotations();
            } else {
                annotations = ((InteractorEditorService) ApplicationContextProvider
                        .getBean("interactorEditorService")).initialiseInteractorAnnotations(interactor);
            }
        } else if (ao instanceof AbstractIntactParticipant) {
            AbstractIntactParticipant participant = (AbstractIntactParticipant) ao;
            if (participant.areAnnotationsInitialized()) {
                annotations = participant.getAnnotations();
            } else {
                annotations = ((ParticipantEditorService) ApplicationContextProvider
                        .getBean("participantEditorService")).initialiseParticipantAnnotations(participant);
            }
        } else if (ao instanceof AbstractIntactFeature) {
            AbstractIntactFeature participant = (AbstractIntactFeature) ao;
            if (participant.areAnnotationsInitialized()) {
                annotations = participant.getAnnotations();
            } else {
                annotations = ((FeatureEditorService) ApplicationContextProvider.getBean("featureEditorService"))
                        .initialiseFeatureAnnotations(participant);
            }
        } else if (ao instanceof IntactCvTerm) {
            IntactCvTerm cv = (IntactCvTerm) ao;
            if (cv.areAnnotationsInitialized()) {
                annotations = cv.getAnnotations();
            } else {
                annotations = getCvService().initialiseCvAnnotations(cv);
            }
        } else if (ao instanceof IntactSource) {
            IntactSource source = (IntactSource) ao;
            if (source.areAnnotationsInitialized()) {
                annotations = source.getAnnotations();
            } else {
                annotations = ((InstitutionService) ApplicationContextProvider.getBean("institutionService"))
                        .initialiseSourceAnnotations(source);
            }
        }

        if (annotations.isEmpty()) {
            return null;
        }
        Annotation caution = AnnotationUtils.collectFirstAnnotationWithTopic(annotations, Annotation.CAUTION_MI,
                Annotation.CAUTION);
        return caution != null ? caution.getValue() : null;
    }

    public String getInternalRemarkMessage() {
        return internalRemark;
    }

    public void setCautionMessage(String cautionMessage) {
        this.cautionMessage = cautionMessage;
    }

    public void setInternalRemark(String internalRemark) {
        this.internalRemark = internalRemark;
    }

    public boolean isUnsavedChanges() {
        return changesController.isUnsaved(getAnnotatedObject());
    }

    public void setUnsavedChanges(boolean unsavedChanges) {
        Collection<String> parentAcs = collectParentAcsOfCurrentAnnotatedObject();

        // we want to add a new change event for this annotated object
        if (unsavedChanges) {
            changesController.markAsUnsaved(getAnnotatedObject(), getDbSynchronizer(), getDescription(), parentAcs);
        }
        // we want to remove any change event concerning this object (or affecting parent and children)
        else {
            changesController.removeFromUnsaved(getAnnotatedObject(), parentAcs);
        }
    }

    public abstract Collection<String> collectParentAcsOfCurrentAnnotatedObject();

    public Date getLastSaved() {
        return lastSaved;
    }

    public void setLastSaved(Date lastSaved) {
        this.lastSaved = lastSaved;
    }

    public CuratorContextController getCuratorContextController() {
        return curatorContextController;
    }

    public CurateController getCurateController() {
        return curateController;
    }

    public boolean canIEditIt() {
        // get the root parent controller
        AnnotatedObjectController parentController = getParentController();
        if (parentController == null) {
            parentController = this;
        } else {
            while (parentController.getParentController() != null) {
                parentController = parentController.getParentController();
            }
        }

        if (parentController.getAnnotatedObject() instanceof Releasable) {
            return getUserSessionController()
                    .isItMe(((Releasable) parentController.getAnnotatedObject()).getCurrentOwner());
        }

        return true;
    }

    public ChangesController getChangesController() {
        return changesController;
    }

    protected boolean isParentObjectNotSaved() {
        AnnotatedObjectController parentController = getParentController();
        if (parentController == null) {
            return false;
        } else {
            if (parentController.getAnnotatedObject() == null) {
                return false;
            } else if (parentController.getAnnotatedObject().getAc() == null) {
                return true;
            }
            // check with the root parent controller
            while (parentController.getParentController() != null) {
                parentController = parentController.getParentController();
                if (parentController.getAnnotatedObject() != null
                        && parentController.getAnnotatedObject().getAc() == null) {
                    return true;
                }
            }
            return false;
        }
    }

    public abstract Class<? extends IntactPrimaryObject> getAnnotatedObjectClass();

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    /**
     * Get the publication ac of this experiment if it exists and add it to the list or parentAcs
     *
     * @param parentAcs
     * @param exp
     */
    public void addPublicationAcToParentAcs(Collection<String> parentAcs, Experiment exp) {
        if (exp != null && exp.getPublication() instanceof IntactPublication) {
            IntactPublication pub = (IntactPublication) exp.getPublication();

            if (pub.getAc() != null) {
                parentAcs.add(pub.getAc());
            }
        }
    }

    /**
     * Get the publication ac of this experiment if it exists, the ac of this experiment if it exists and add it to the list or parentAcs
     *
     * @param parentAcs
     * @param exp
     */
    public void addParentAcsTo(Collection<String> parentAcs, IntactExperiment exp) {
        if (exp != null && exp.getAc() != null) {
            parentAcs.add(exp.getAc());
        }

        addPublicationAcToParentAcs(parentAcs, exp);
    }

    public boolean isAnnotationTopicDisabled() {
        return isAnnotationTopicDisabled;
    }

    public void setAnnotationTopicDisabled(boolean annotationTopicDisabled) {
        isAnnotationTopicDisabled = annotationTopicDisabled;
    }

    public boolean isXrefDisabled() {
        return isXrefDisabled;
    }

    public void setXrefDisabled(boolean xrefDisabled) {
        isXrefDisabled = xrefDisabled;
    }

    public boolean isAliasDisabled() {
        return isAliasDisabled;
    }

    public void setAliasDisabled(boolean aliasDisabled) {
        isAliasDisabled = aliasDisabled;
    }

    public abstract boolean isAliasNotEditable(Alias alias);

    public abstract boolean isAnnotationNotEditable(Annotation annot);

    public abstract boolean isXrefNotEditable(Xref ref);

    /**
     * Bug jsf : selectOneMenu in a tab returns null if not active tab so we disable the selectOneMenu when it is disabled
     *
     * @param e
     */
    public void onTabChanged(TabChangeEvent e) {

        // the xref tab is active
        if (e.getTab().getId().equals("xrefsTab")) {
            isXrefDisabled = false;
            isAliasDisabled = true;
            isAnnotationTopicDisabled = true;
        } else if (e.getTab().getId().equals("annotationsTab")) {
            isXrefDisabled = true;
            isAliasDisabled = true;
            isAnnotationTopicDisabled = false;
        } else if (e.getTab().getId().equals("aliasesTab")) {
            isXrefDisabled = true;
            isAliasDisabled = false;
            isAnnotationTopicDisabled = true;
        } else {
            isXrefDisabled = true;
            isAliasDisabled = true;
            isAnnotationTopicDisabled = true;
        }
    }

    public abstract IntactDbSynchronizer getDbSynchronizer();

    public abstract String getObjectName();

    public String getObjectCategory() {
        return curatorContextController.intactObjectSimpleName(getAnnotatedObject());
    }

    public String getTitle() {
        if (getAnnotatedObject() != null) {
            return getObjectCategory() + ": " + getObjectName() + " | Curate | Editor";
        } else {
            return " | Curate | Editor";
        }
    }

    public abstract String getAc();

    public abstract int getXrefsSize();

    public abstract int getAliasesSize();

    public abstract int getAnnotationsSize();

    public EditorObjectService getEditorService() {
        if (this.editorService == null) {
            this.editorService = ApplicationContextProvider.getBean("editorObjectService");
        }
        return editorService;
    }

    public OntologyTermFetcher getGoServerProxy() throws BridgeFailedException {
        if (this.goServerProxy == null) {
            this.goServerProxy = new CachedOlsOntologyTermFetcher();
        }
        return goServerProxy;
    }

    public CvObjectService getCvService() {
        if (this.cvService == null) {
            this.cvService = ApplicationContextProvider.getBean("cvObjectService");
        }
        return cvService;
    }

    public static class AuditableComparator<T extends Auditable> implements Comparator<T> {

        @Override
        public int compare(T auditable, T auditable2) {
            if (auditable == null && auditable2 == null) {
                return 0;
            } else if (auditable == null) {
                return 1;
            } else if (auditable2 == null) {
                return -1;
            } else {
                Date created1 = auditable.getCreated();
                Date created2 = auditable2.getCreated();
                if (created1 == null && created2 == null) {
                    return 0;
                } else if (created1 == null) {
                    return -1;
                } else if (created2 == null) {
                    return 1;
                } else {
                    return -created1.compareTo(created2);
                }
            }
        }
    }

    public static class XrefComparator<T> implements Comparator<T> {

        @Override
        public int compare(T xrefObject1, T xrefObject2) {
            if (xrefObject1 == null && xrefObject2 == null) {
                return 0;
            } else if (xrefObject1 == null) {
                return 1;
            } else if (xrefObject2 == null) {
                return -1;
            } else {
                try {
                    return ((Xref) xrefObject1).getId().compareTo(((Xref) xrefObject2).getId());
                } catch (Exception e) {
                    return 0;
                }
            }

        }
    }

    public CvTerm getNewDatabase() {
        return newDatabase;
    }

    public void setNewDatabase(CvTerm newDatabase) {
        this.newDatabase = newDatabase;
    }

    public String getNewXrefId() {
        return newXrefId;
    }

    public void setNewXrefId(String newXrefId) {
        this.newXrefId = newXrefId;
    }

    public String getNewSecondaryId() {
        return newSecondaryId;
    }

    public void setNewSecondaryId(String newSecondaryId) {
        this.newSecondaryId = newSecondaryId;
    }

    public CvTerm getNewQualifier() {
        return newQualifier;
    }

    public void setNewQualifier(CvTerm newQualifier) {
        this.newQualifier = newQualifier;
    }

    public String getNewXrefVersion() {
        return newXrefVersion;
    }

    public void setNewXrefVersion(String newXrefVersion) {
        this.newXrefVersion = newXrefVersion;
    }

    public CvTerm getNewAliasType() {
        return newAliasType;
    }

    public void setNewAliasType(CvTerm newAliasType) {
        this.newAliasType = newAliasType;
    }

    public String getNewAliasName() {
        return newAliasName;
    }

    public void setNewAliasName(String newAliasName) {
        this.newAliasName = newAliasName;
    }

    public CvTerm getNewTopic() {
        return newTopic;
    }

    public void setNewTopic(CvTerm newTopic) {
        this.newTopic = newTopic;
    }

    public String getNewAnnotationDescription() {
        return newAnnotationDescription;
    }

    public void setNewAnnotationDescription(String newAnnotationDescription) {
        this.newAnnotationDescription = newAnnotationDescription;
    }
}