Java tutorial
/* * The Gemma project * * Copyright (c) 2007 University of British Columbia * * 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 ubic.gemma.core.loader.expression.geo.service; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.w3c.dom.Document; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import ubic.gemma.core.loader.entrez.EutilFetch; import ubic.gemma.core.loader.expression.geo.model.GeoRecord; import ubic.gemma.model.common.auditAndSecurity.AuditEvent; import ubic.gemma.model.common.description.DatabaseEntry; import ubic.gemma.model.common.description.ExternalDatabase; import ubic.gemma.model.expression.arrayDesign.ArrayDesign; import ubic.gemma.model.expression.experiment.ExpressionExperiment; import ubic.gemma.model.genome.Taxon; import ubic.gemma.persistence.service.common.description.ExternalDatabaseService; import ubic.gemma.persistence.service.expression.arrayDesign.ArrayDesignService; import ubic.gemma.persistence.service.expression.experiment.ExpressionExperimentService; import ubic.gemma.persistence.service.genome.taxon.TaxonService; import ubic.gemma.persistence.util.Settings; import javax.annotation.PostConstruct; import javax.xml.parsers.ParserConfigurationException; import javax.xml.xpath.*; import java.io.*; import java.text.ParseException; import java.util.*; /** * @author pavlidis */ @Component public class GeoBrowserServiceImpl implements GeoBrowserService { private static final int MIN_SAMPLES = 5; private static final String GEO_DATA_STORE_FILE_NAME = "GEODataStore"; private static final Log log = LogFactory.getLog(GeoBrowserServiceImpl.class.getName()); @Autowired protected ExpressionExperimentService expressionExperimentService; @Autowired protected ArrayDesignService arrayDesignService; @Autowired private TaxonService taxonService; @Autowired private ExternalDatabaseService externalDatabaseService; private Map<String, GeoRecord> localInfo; private XPathExpression xgse; private XPathExpression xtitle; private XPathExpression xgpls; private XPathExpression xsummary; @PostConstruct public void afterPropertiesSet() throws Exception { this.initializeLocalInfo(); XPathFactory xf = XPathFactory.newInstance(); XPath xpath = xf.newXPath(); // xgds = xpath.compile( "/eSummaryResult/DocSum/Item[@Name=\"GDS\"][1]/text()" ); xgse = xpath.compile("/eSummaryResult/DocSum/Item[@Name=\"GSE\"][1]/text()"); xtitle = xpath.compile("/eSummaryResult/DocSum/Item[@Name=\"title\"][1]/text()"); xgpls = xpath.compile("/eSummaryResult/DocSum/Item[@Name=\"GPL\"]/text()"); xsummary = xpath.compile("/eSummaryResult/DocSum/Item[@Name=\"summary\"][1]/text()"); } @Override public String getDetails(String accession) throws IOException { /* * The maxrecords is > 1 because it return platforms as well (and there are series with as many as 13 platforms * ... leaving some headroom) */ String details = EutilFetch.fetch("gds", accession, 25); if (details == null) { throw new IOException("No results from GEO"); } this.initLocalRecord(accession); /* * increment click counts. */ localInfo.get(accession).setPreviousClicks(localInfo.get(accession).getPreviousClicks() + 1); this.saveLocalInfo(); return this.formatDetails(details); } @Override public List<GeoRecord> getRecentGeoRecords(int start, int count) throws IOException, ParseException { GeoBrowser browser = new GeoBrowser(); List<GeoRecord> records = browser.getRecentGeoRecords(start, count); if (records.isEmpty()) return records; return this.filterGeoRecords(records); } @Override public List<GeoRecord> searchGeoRecords(String searchString, int start, int count) throws IOException { GeoBrowser browser = new GeoBrowser(); // Change this method to browser.getGeoRecordsBySearchTerm when implemented in GeoBrowser.java List<GeoRecord> records = browser.getGeoRecordsBySearchTerm(searchString, start, count); return this.filterGeoRecords(records); } @Override public boolean toggleUsability(String accession) { this.initLocalRecord(accession); localInfo.get(accession).setUsable(!localInfo.get(accession).isUsable()); this.saveLocalInfo(); return localInfo.get(accession).isUsable(); } /** * Take the details string from GEO and make it nice. Add links to series and platforms that are already in gemma. * * @param details XML from eSummary * @return HTML-formatted */ String formatDetails(String details) throws IOException { /* * Bug 2690. There must be a better way. */ details = details.replaceAll("encoding=\"UTF-8\"", ""); try { Document document = EutilFetch.parseStringInputStream(details); String gse = "GSE" + xgse.evaluate(document, XPathConstants.STRING); String title = (String) xtitle.evaluate(document, XPathConstants.STRING); NodeList gpls = (NodeList) xgpls.evaluate(document, XPathConstants.NODESET); String summary = (String) xsummary.evaluate(document, XPathConstants.STRING); StringBuilder buf = new StringBuilder(); buf.append("<div class=\"small\">"); ExpressionExperiment ee = this.expressionExperimentService.findByShortName(gse); if (ee != null) { buf.append("\n<p><strong><a target=\"_blank\" href=\"").append(Settings.getRootContext()) .append("/expressionExperiment/showExpressionExperiment.html?id=").append(ee.getId()) .append("\">").append(gse).append("</a></strong>"); } else { buf.append("\n<p><strong>").append(gse).append(" [new to Gemma]</strong>"); } buf.append("<p>").append(title).append("</p>\n"); buf.append("<p>").append(summary).append("</p>\n"); this.formatArrayDetails(gpls, buf); buf.append("</div>"); details = buf.toString(); // } } catch (ParserConfigurationException | SAXException | XPathExpressionException e) { throw new RuntimeException(e); } return details; } private List<GeoRecord> filterGeoRecords(List<GeoRecord> records) { ExternalDatabase geo = externalDatabaseService.findByName("GEO"); Collection<GeoRecord> toRemove = new HashSet<>(); assert geo != null; rec: for (GeoRecord record : records) { if (record.getNumSamples() < GeoBrowserServiceImpl.MIN_SAMPLES) { toRemove.add(record); } Collection<String> organisms = record.getOrganisms(); if (organisms == null || organisms.size() == 0) { continue; } int i = 0; for (String string : organisms) { Taxon t = taxonService.findByCommonName(string); if (t == null) { t = taxonService.findByScientificName(string); if (t == null) { toRemove.add(record); continue rec; } } String acc = record.getGeoAccession(); if (organisms.size() > 1) { acc = acc + "." + i; } DatabaseEntry de = DatabaseEntry.Factory.newInstance(); de.setExternalDatabase(geo); de.setAccession(acc); Collection<ExpressionExperiment> ee = expressionExperimentService.findByAccession(de); if (!ee.isEmpty()) { for (ExpressionExperiment expressionExperiment : ee) { record.getCorrespondingExperiments().add(expressionExperiment.getId()); } } record.setPreviousClicks(localInfo.containsKey(acc) ? localInfo.get(acc).getPreviousClicks() : 0); record.setUsable(!localInfo.containsKey(acc) || localInfo.get(acc).isUsable()); i++; } } records.removeAll(toRemove); return records; } private void formatArrayDetails(NodeList gpls, StringBuilder buf) { Set<String> seenGpl = new HashSet<>(); for (int i = 0; i < gpls.getLength(); i++) { String gpl = "GPL" + gpls.item(i).getNodeValue(); if (gpl.contains(";")) continue; if (seenGpl.contains(gpl)) continue; seenGpl.add(gpl); ArrayDesign arrayDesign = arrayDesignService.findByShortName(gpl); if (arrayDesign != null) { arrayDesign = arrayDesignService.thawLite(arrayDesign); String trouble = ""; if (arrayDesign.getCurationDetails().getTroubled()) { AuditEvent lastTroubleEvent = arrayDesign.getCurationDetails().getLastTroubledEvent(); if (lastTroubleEvent != null) { trouble = " <img src='" + Settings.getRootContext() + "/images/icons/warning.png' height='16' width='16' alt=\"troubled\" title=\"" + lastTroubleEvent.getNote() + "\"/>"; } } buf.append("<p><strong>Platform in Gemma: <a target=\"_blank\" href=\"") .append(Settings.getRootContext()).append("/arrays/showArrayDesign.html?id=") .append(arrayDesign.getId()).append("\">").append(gpl).append("</a></strong>") .append(trouble); } else { buf.append("<p><strong>").append(gpl).append(" [New to Gemma]</strong>"); } } } private File getInfoStoreFile() { String path = Settings.getDownloadPath(); return new File(path + File.separatorChar + GeoBrowserServiceImpl.GEO_DATA_STORE_FILE_NAME); } @SuppressWarnings("unchecked") private void initializeLocalInfo() { File f = this.getInfoStoreFile(); if (f.exists()) { try (FileInputStream fis = new FileInputStream(f); ObjectInputStream ois = new ObjectInputStream(fis)) { //noinspection unchecked this.localInfo = (Map<String, GeoRecord>) ois.readObject(); } catch (Exception e) { GeoBrowserServiceImpl.log .error("Failed to load local GEO info from " + f.getAbsolutePath() + ", reinitializing..."); this.localInfo = new HashMap<>(); this.saveLocalInfo(); // ensure this gets initialized even if unused } } else { this.localInfo = new HashMap<>(); } assert this.localInfo != null; } private void initLocalRecord(String accession) { assert localInfo != null; if (!localInfo.containsKey(accession)) { localInfo.put(accession, new GeoRecord()); localInfo.get(accession).setGeoAccession(accession); } } /** * Save the cached GEO information for next time */ private void saveLocalInfo() { if (this.localInfo == null) return; try (FileOutputStream fos = new FileOutputStream(this.getInfoStoreFile()); ObjectOutputStream oos = new ObjectOutputStream(fos)) { oos.writeObject(this.localInfo); oos.flush(); } catch (Exception e) { GeoBrowserServiceImpl.log.error("Failed to save local GEO info", e); } } }