Java tutorial
/* * Computoser is a music-composition algorithm and a website to present the results * Copyright (C) 2012-2014 Bozhidar Bozhanov * * Computoser is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * Computoser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with Computoser. If not, see <http://www.gnu.org/licenses/>. */ package com.music.service.text; import java.io.PrintStream; import java.util.Properties; import java.util.Set; import javax.annotation.PostConstruct; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import com.music.model.persistent.TimelineMusic; import edu.stanford.nlp.ling.CoreAnnotations; import edu.stanford.nlp.neural.rnn.RNNCoreAnnotations; import edu.stanford.nlp.pipeline.Annotation; import edu.stanford.nlp.pipeline.StanfordCoreNLP; import edu.stanford.nlp.sentiment.SentimentCoreAnnotations; import edu.stanford.nlp.trees.Tree; import edu.stanford.nlp.util.CoreMap; @Service public class SentimentAnalyzer { private static final Logger logger = LoggerFactory.getLogger(SentimentAnalyzer.class); private StanfordCoreNLP pipeline = new StanfordCoreNLP(getProperties()); @PostConstruct public void init() { // because scientists can't code, and write debug messages to System.err PrintStream err = new PrintStream(System.err) { @Override public void println(String x) { if (!x.startsWith("Adding annotator")) { super.println(x); } } }; System.setErr(err); } private Properties getProperties() { Properties props = new Properties(); props.setProperty("annotators", "tokenize, ssplit, parse, sentiment"); return props; } /** * Synchronized method to obtain the sentiment of the set of documents. * Synchronization is fine, because the method is invoked via a scheduled job * and only one execution at a time is permitted. * That allows to optimize the loading of the model as well * @param documents * @return */ public synchronized SentimentResult getSentiment(Set<String> documents, TimelineMusic meta) { double sentimentSum = 0; for (String document : documents) { int mainSentiment = 0; if (document != null && document.length() > 0) { int longest = 0; try { Annotation annotation = pipeline.process(document); // mainSentiment is the sentiment of the whole document. We find // the whole document by comparing the length of individual // annotated "fragments" for (CoreMap sentence : annotation.get(CoreAnnotations.SentencesAnnotation.class)) { Tree tree = sentence.get(SentimentCoreAnnotations.AnnotatedTree.class); int sentiment = RNNCoreAnnotations.getPredictedClass(tree); String partText = sentence.toString(); if (partText.length() > longest) { mainSentiment = sentiment; longest = partText.length(); } } } catch (Exception ex) { logger.error("Problem analyzing document sentiment. " + document, ex); continue; } } sentimentSum += mainSentiment; } double average = sentimentSum / documents.size(); meta.setAverageSentiment(average); if (average >= 2.25) { return SentimentResult.POSITIVE; } else if (average <= 1.75) { return SentimentResult.NEGATIVE; } return SentimentResult.NEUTRAL; } public static enum SentimentResult { POSITIVE, NEGATIVE, NEUTRAL } }