package org.gmod.schema.feature; import org.genedb.db.analyzers.AllNamesAnalyzer; import org.genedb.db.loading.EmblLocation; import org.gmod.schema.mapped.Feature; import org.gmod.schema.mapped.FeatureLoc; import org.gmod.schema.mapped.FeatureRelationship; import org.gmod.schema.mapped.Organism; import org.apache.log4j.Logger; import org.hibernate.Session; import org.hibernate.search.annotations.Analyzer; import org.hibernate.search.annotations.Field; import org.hibernate.search.annotations.Index; import org.hibernate.search.annotations.Store; import org.springframework.orm.hibernate3.SessionFactoryUtils; import java.lang.reflect.InvocationTargetException; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Collection; import java.util.List; import javax.persistence.Entity; import javax.persistence.Transient; /** * A {@link Gene} or a {@link Pseudogene}. * * @author rh11 * */ @SuppressWarnings("serial") @Entity public abstract class AbstractGene extends TopLevelFeature { private static final Logger logger = Logger.getLogger(AbstractGene.class); AbstractGene() { // empty } public AbstractGene(Organism organism, String uniqueName, boolean analysis, boolean obsolete, Timestamp dateAccessioned) { super(organism, uniqueName, analysis, obsolete, dateAccessioned); } @Transient @Field(name = "gene", index = Index.UN_TOKENIZED, store = Store.YES) public String getGeneUniqueName() { return getUniqueName(); } private transient Transcript firstTranscripts; /** * Get a collection of this gene's transcripts. * @return a collection of this gene's transcripts */ @Transient public Collection<Transcript> getTranscripts() { Collection<Transcript> ret = new ArrayList<Transcript>(); for (FeatureRelationship relationship : this.getFeatureRelationshipsForObjectId()) { Feature transcript = relationship.getSubjectFeature(); if (transcript instanceof Transcript) { ret.add((Transcript) transcript); } } return ret; } /** * Get a collection of this gene's transcripts. that aren't obsolete. * @return a collection of this gene's transcripts that aren't obsolete */ @Transient public Collection<Transcript> getNonObsoleteTranscripts() { Collection<Transcript> ret = new ArrayList<Transcript>(); for (FeatureRelationship relationship : this.getFeatureRelationshipsForObjectId()) { Feature transcript = relationship.getSubjectFeature(); if (transcript instanceof Transcript && transcript.isObsolete() == false) { ret.add((Transcript) transcript); } } return ret; } @Transient @Field(name = "alternateTranscriptNumber", index = Index.UN_TOKENIZED, store = Store.YES) public int alternateTranscriptNumber() { return getNonObsoleteTranscripts().size(); } @Transient @Field(name = "alternateTranscripts", index = Index.UN_TOKENIZED, store = Store.YES) public String alternateTranscripts() { StringBuffer alternateTranscripts = new StringBuffer(); for (Transcript t : getNonObsoleteTranscripts()) { alternateTranscripts.append(t.getUniqueName()); } return alternateTranscripts.toString(); } @Transient @Analyzer(impl = AllNamesAnalyzer.class) @Field(name = "product", index = Index.TOKENIZED, store = Store.YES) public String getProductsAsSpaceSeparatedString() { if (getFirstTranscript() != null) { return getFirstTranscript().getProductsAsSpaceSeparatedString(); } return null; } @Transient public Transcript getFirstTranscript() { if (firstTranscripts != null) { return firstTranscripts; } List<Transcript> temp = new ArrayList<Transcript>(); for (FeatureRelationship relationship : this.getFeatureRelationshipsForObjectId()) { Feature transcript = relationship.getSubjectFeature(); if (transcript instanceof Transcript) { temp.add((Transcript) transcript); } } //find first item in sorted list if (temp.size() > 1){ Transcript tempTanscript = temp.get(0); for(Transcript t : temp){ if (tempTanscript.getUniqueName().compareTo(t.getUniqueName()) > 0){ tempTanscript = t; } } firstTranscripts = tempTanscript; }else if (temp.size() == 1){ firstTranscripts = temp.get(0); } return firstTranscripts; } /** * Is this a pseudogene? * @return <code>true</code> if this is a pseudogene, or <code>false</code> if not */ @Transient public boolean isPseudo() { return (this instanceof Pseudogene); } /** * Rather than returning the residues stored in the database, this method * extracts the appropriate subsequence of the residues of the source * feature (chromosome, supercontig or contig). */ @Transient @Override public String getResidues() { Feature parent = this.getRankZeroFeatureLoc().getSourceFeature(); return parent.getResidues(this.getStart(), this.getStop()); } public static <T extends AbstractGene> T make(Class<T> geneClass, Organism organism, String uniqueName, String name) { try { return geneClass.getDeclaredConstructor(Organism.class, String.class, String.class).newInstance(organism, uniqueName, name); } catch (IllegalArgumentException e) { throw new RuntimeException("Internal error: failed to construct gene", e); } catch (SecurityException e) { throw new RuntimeException("Internal error: failed to construct gene", e); } catch (InstantiationException e) { throw new RuntimeException("Internal error: failed to construct gene", e); } catch (IllegalAccessException e) { throw new RuntimeException("Internal error: failed to construct gene", e); } catch (InvocationTargetException e) { throw new RuntimeException("Internal error: failed to construct gene", e); } catch (NoSuchMethodException e) { throw new RuntimeException("Internal error: failed to construct gene", e); } } /** * Create a transcript for this gene, which is assumed to be newly-created * and not to have a transcript yet. * * @param <T> * @param transcriptClass the class of the transcript to create * @param transcriptUniqueName the uniqueName of the transcript to create * @param fmin * @param fmax * @return */ public <T extends Transcript> T makeTranscript(Class<T> transcriptClass, String transcriptUniqueName, int fmin, int fmax) { T transcript = makeTranscript(transcriptClass, transcriptUniqueName, fmin, fmax, null, null); //don't need to supply the gene and EmblLocation for transcripts located on normal topLevel features return transcript; } /** * Create a transcript for this gene, which is assumed to be newly-created * and not to have a transcript yet. * *For transcripts located on topLevel genes the gene and EmblLocation objects *are required for featureLocing * * @param <T> * @param transcriptClass the class of the transcript to create * @param transcriptUniqueName the uniqueName of the transcript to create * @param fmin * @param fmax * @param gene the gene feature if it is a topLevel gene (else null) * @param location the EmblLocation object if the gene is topLevel (else null) * @return */ public <T extends Transcript> T makeTranscript(Class<T> transcriptClass, String transcriptUniqueName, int fmin, int fmax, AbstractGene gene, EmblLocation location) { Session session = SessionFactoryUtils.getSession(sessionFactory, false); T transcript = Transcript.construct(transcriptClass, getOrganism(), transcriptUniqueName, null); session.persist(transcript); int relativeFmin = 0; int relativeFmax = 0; if (gene != null && gene.isTopLevelFeature()) { //gene is topLevel so featureLoc transcript onto gene logger.trace(String.format("Creating transcript '%s' for gene '%s' at locations %d..%d on a topLevel gene", transcriptUniqueName, getUniqueName(), fmin, fmax)); gene.addLocatedChild(transcript, fmin, fmax, location.getStrand(), null /*phase*/); } else { //normal genes on normal topLevel features logger.trace(String.format("Creating transcript '%s' for gene '%s' at locations %d..%d (gene locations %d..%d)", transcriptUniqueName, getUniqueName(), fmin, fmax, getFmin(), getFmax())); if (fmax < fmin) { throw new IllegalArgumentException(String.format("fmax (%d) < fmin (%d)", fmax, fmin)); } relativeFmin = fmin - this.getFmin(); if (relativeFmin < 0) { logger.trace(String.format("Start of transcript (%d) is before start of gene (%d). Resetting gene start", fmin, this.getFmin())); this.lowerFminTo(fmin); } relativeFmax = fmax - this.getFmin(); if (fmax > this.getFmax()) { logger.trace(String.format("End of transcript (%d) is after end of gene (%d). Resetting gene end", fmax, this.getFmax())); this.raiseFmaxTo(fmax); } for (FeatureLoc featureLoc: getFeatureLocs()) { featureLoc.getSourceFeature().addLocatedChild(transcript, featureLoc.getFmin() + relativeFmin, featureLoc.getFmin() + relativeFmax, featureLoc.getStrand(), null /*phase*/, featureLoc.getLocGroup(), featureLoc.getRank()); } } this.addFeatureRelationship(transcript, "relationship", "part_of"); if (ProductiveTranscript.class.isAssignableFrom(transcriptClass)) { String polypeptideUniqueName; if (transcriptUniqueName.endsWith(":mRNA")) { polypeptideUniqueName = String.format("%s:pep", transcriptUniqueName.substring(0, transcriptUniqueName.length() - 5)); } else { polypeptideUniqueName = String.format("%s:pep", transcriptUniqueName); } logger.debug(String.format("Creating polypeptide '%s' for transcript '%s'", polypeptideUniqueName, getUniqueName())); Polypeptide polypeptide = new Polypeptide(getOrganism(), polypeptideUniqueName); session.persist(polypeptide); for (FeatureLoc featureLoc: transcript.getFeatureLocs()) { featureLoc.getSourceFeature().addLocatedChild(polypeptide, featureLoc.getFmin(), featureLoc.getFmax(), featureLoc.getStrand(), null /*phase*/, featureLoc.getLocGroup(), featureLoc.getRank()); } ((ProductiveTranscript)transcript).setProtein(polypeptide); } return transcript; } }