package org.genedb.web.mvc.controller.download; import java.io.IOException; import java.io.Writer; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.log4j.Logger; import org.genedb.db.dao.SequenceDao; import org.genedb.querying.tmpquery.GeneDetail; import org.gmod.schema.mapped.Feature; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionTemplate; /** * * A base for formatter class collating common properties. * * @author gv1 */ public abstract class FormatBase { private Logger logger = Logger.getLogger(FormatBase.class); protected String fieldSeparator = ""; protected String fieldInternalSeparator = ""; protected String recordSeparator = ""; protected String postFieldSeparator = ""; protected String postFieldInternalSeparator = ""; protected String postRecordSeparator = ""; protected String headerContentStart = ""; protected String footerContentStart = ""; protected String blankField = ""; protected List<OutputOption> outputOptions = new ArrayList<OutputOption>(); protected boolean header; protected Writer writer; private SequenceDao sequenceDao; protected TransactionTemplate transactionTemplate; public void setTransactionTemplate(TransactionTemplate transactionTemplate) { this.transactionTemplate = transactionTemplate; } public void setFieldSeparator(String fieldSeparator) { this.fieldSeparator = fieldSeparator; } public void setPostFieldSeparator(String postFieldSeparator) { this.postFieldSeparator = postFieldSeparator; } public void setFieldInternalSeparator(String fieldInternalSeparator) { this.fieldInternalSeparator = fieldInternalSeparator; } public void setPostFieldInternalSeparator(String postFieldInternalSeparator) { this.postFieldInternalSeparator = postFieldInternalSeparator; } public void setBlankField(String blankField) { this.blankField = blankField; } public void setRecordSeparator(String recordSeparator) { this.recordSeparator = recordSeparator; } public void setPostRecordSeparator(String postRecordSeparator) { this.postRecordSeparator = postRecordSeparator; } public void setOutputOptions(List<OutputOption> outputOptions) { this.outputOptions = outputOptions; } public void setWriter(Writer writer) { this.writer = writer; } public void setHeader(boolean header) { this.header = header; } public void setHeaderContentStart(String headerContentStart) { this.headerContentStart = headerContentStart; } public void setFooterContentStart(String footerContentStart) { this.footerContentStart = footerContentStart; } // private BerkeleyMapFactory bmf; // // public void setBmf(BerkeleyMapFactory bmf) { // this.bmf = bmf; // } public void setSequenceDao(SequenceDao sequenceDao) { this.sequenceDao = sequenceDao; } private Map<String, Feature> features; private void getFeatures(List<String> uniqueNames) { features = new HashMap<String, Feature>(); List<Feature> allFeatures = sequenceDao.getFeaturesByUniqueNames(uniqueNames); for (Feature feature : allFeatures) { String uniqueName = feature.getUniqueName(); features.put(uniqueName, feature); } } /** * Checks to see if all the options set for this formatter can be run only using Lucene. * */ protected boolean onlyNeedLuceneLookups() { boolean available = true; for (OutputOption outputOption : outputOptions) { if (! GeneDetailFieldValueExctractor.availableFromLucene(outputOption)) { available = false; } } return available; } /** * Any set of entries that contains something other than mRNA requires features. * @param entries * @return */ protected boolean requireFeatures(List<GeneDetail> entries) { for (GeneDetail entry : entries) { if (! entry.getType().equals("mRNA")) { return true; } } return false; } /** * Formats the results, with headers and footers. Pages through the entries (to speed up Feature lookups by chunking them). * * @param entries * @throws IOException */ public void format(final List<GeneDetail> entries) throws IOException { formatHeader(); final int max = entries.size() -1; int start = 0; int window = 1000; int stop = start + window; final boolean allOptionsAvailableFromLucene = onlyNeedLuceneLookups(); //int count = 0; while (start <= max) { if (stop > max) { // List.sublist, the end coordinate is NOT inclusive stop = max + 1; } if (start > max) { break; } logger.debug(String.format("%s :: %s", start, stop)); final int final_start = start; final int final_stop = stop; transactionTemplate.execute(new TransactionCallback<Object>() { @Override public Object doInTransaction(TransactionStatus status) { final List<GeneDetail> currentEntries = entries.subList(final_start, final_stop); //count += currentEntries.size(); if (! allOptionsAvailableFromLucene) { if (requireFeatures(currentEntries)) { List<String> uniqueNames = new ArrayList<String>(); for (GeneDetail detail : currentEntries) { uniqueNames.add(detail.getSystematicId()); } getFeatures(uniqueNames); } else { // make sure we reset the features map... (no need to carry a live instance of it when it's no longer needed) features = null; } } try { formatBody(currentEntries); } catch (IOException e) { e.printStackTrace(); logger.error(e.getMessage()); } return true; } }); start += window; stop = start + window; } // logger.debug(String.format("%s==%s", count, entries.size())); formatFooter(); logger.info("Export complete"); } abstract public void formatHeader() throws IOException; abstract public void formatBody(List<GeneDetail> entries) throws IOException; abstract public void formatFooter() throws IOException; protected GeneDetailFieldValueExctractor getExtractor(GeneDetail entry) { return new GeneDetailFieldValueExctractor(entry, sequenceDao, features, fieldInternalSeparator, blankField); } protected List<String> getFieldValues(GeneDetailFieldValueExctractor extractor, List<OutputOption> outputOptions) { List<String> values = new ArrayList<String>(); for (OutputOption outputOption : outputOptions) { values.add(extractor.getFieldValue(outputOption)); } return values; } }