package org.cogroo.ruta.checker;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.log4j.Logger;
import org.apache.uima.analysis_engine.AnalysisEngine;
import org.apache.uima.analysis_engine.AnalysisEngineProcessException;
import org.apache.uima.cas.CAS;
import org.apache.uima.cas.FSIndex;
import org.apache.uima.cas.Feature;
import org.apache.uima.cas.Type;
import org.apache.uima.cas.TypeSystem;
import org.apache.uima.cas.text.AnnotationFS;
import org.cogroo.analyzer.Analyzer;
import org.cogroo.analyzer.ComponentFactory;
import org.cogroo.checker.CheckDocument;
import org.cogroo.checker.GrammarChecker;
import org.cogroo.entities.Mistake;
import org.cogroo.entities.Sentence;
import org.cogroo.entities.Token;
import org.cogroo.ruta.tools.RuleParser;
import org.cogroo.ruta.uima.AEFactory;
import org.cogroo.ruta.uima.AnnotatorUtil;
import org.cogroo.ruta.uima.UimaCasAdapter;
import org.cogroo.tools.checker.AbstractTypedChecker;
import org.cogroo.tools.checker.RuleDefinition;
import org.cogroo.tools.checker.rules.applier.SuggestionBuilder;
import org.cogroo.tools.checker.rules.dictionary.CogrooTagDictionary;
import org.cogroo.tools.checker.rules.model.Example;
import org.cogroo.tools.checker.rules.model.TagMask;
import org.cogroo.tools.checker.rules.util.TagMaskUtils;
import opennlp.tools.util.Span;
public class UIMAChecker extends AbstractTypedChecker {
private static final Logger LOGGER = Logger.getLogger(UIMAChecker.class);
private final AnalysisEngine ae;
private final UimaCasAdapter converter;
private Type mProblemType;
private Type mTokenType;
private Feature mIDFeature, mSuggestionFeature;
private final boolean developing = false;
private static final Pattern SWAP = Pattern
.compile("swap\\s+(\\d+)\\s+(\\d+)");
private static final Pattern REPLACE_LEXEME = Pattern
.compile("replace\\s+(\\d+)\\s+with\\s+'([^']+)'");
private static final Pattern REPLACE_MAPPING = Pattern
.compile("replace\\s+(\\d+)\\s+with\\s*\\{((\\s*'[^']+'\\s*=>\\s*'[^']+'\\s*)+)\\}");
private static final Pattern REPLACE_MAPPING2 = Pattern
.compile("'([^']+)'\\s*=>\\s*'([^']+)'");
private static final Pattern REPLACE_TAGR = Pattern
.compile("replace\\s+(\\d+)\\s+with\\s+(\\d+)\\s+in\\s+\\(((\\s*(number|gender|class|person|tense|mood)\\s*=\\s*[\\w-]+\\s*)+)\\)");
private static final Pattern REPLACE_R = Pattern
.compile("set\\s+\\((\\s*(gender|number|class|person|tense|mood)\\s*)+\\)\\s+of\\s+(\\d+)\\s+to\\s+match\\s+(\\d+)");
private final HashSet<Integer> done = new HashSet<Integer>();
private final CogrooTagDictionary tagDictionary;
private final SuggestionBuilder suggestionBuilder;
public UIMAChecker(CogrooTagDictionary td) {
this.tagDictionary = td;
this.suggestionBuilder = new SuggestionBuilder(this.tagDictionary);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Initializing UIMA Checker constructor.");
}
this.ae = AEFactory.createRutaAE();
String fileName = "cogroo/ruta/Regras.txt";
for (RuleDefinition ruleDef : RuleParser.getRuleDefinitionList(fileName))
add(ruleDef);
this.converter = new UimaCasAdapter();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("UIMA Checker constructor finished.");
}
}
private static String applyCase(String s, int type) {
switch (type) {
case 0:
return s.toLowerCase();
case 1:
return Character.toUpperCase(s.charAt(0)) + s.substring(1);
case 2:
return s.toUpperCase();
default:
throw new IllegalArgumentException();
}
}
private static int getCase(String s) {
char first = s.charAt(0);
char second = s.length() > 1 ? s.charAt(1) : 'a';
// if a word has only one letter, suppose the "other letters" are
// lower-case.
if (Character.isLowerCase(first))
return 0;
else if (Character.isUpperCase(second))
return 2;
else
return 1;
}
@Override
public List<Mistake> check(Sentence sentence) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String.format("Checking sentence: '%s'", sentence));
}
// TODO: added for now in order to prevent this method to run more than
// once
// if (done.contains(sentence.getOffset()))
// System.exit(0);
done.add(sentence.getOffset());
List<Mistake> mistakes = new LinkedList<Mistake>();
try {
CAS cas = ae.newCAS();
// http://article.gmane.org/gmane.comp.apache.uima.general/6274
cas.setDocumentText(sentence.getDocumentText());
converter.populateCas(sentence.getTextSentence(), cas);
ae.process(cas);
TypeSystem typeSystem = cas.getTypeSystem();
initTypeSystem(typeSystem);
Set<Integer> processed = new HashSet<Integer>();
FSIndex<AnnotationFS> problems = cas
.getAnnotationIndex(mProblemType);
for (AnnotationFS problem : problems) {
if (processed.contains(problem.getBegin()))
continue;
else
processed.add(problem.getBegin());
List<Token> coveredTokens = getTokens(sentence,
problem.getBegin(), problem.getEnd());
List<String> suggestions = new ArrayList<String>(4);
String id = problem.getFeatureValueAsString(mIDFeature);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Found a mistake, id = " + id);
}
RuleDefinition rd = getRuleDefinition(id);
System.out.println("\nID = '" + id + "'");
System.out.println("TEXTO DO PROBLEM: '"
+ problem.getCoveredText() + "'");
System.out.println("MENSAGEM: '" + rd.getMessage() + "'");
System.out.println("MENSAGEM CURTA: '" + rd.getShortMessage()
+ "'");
System.out.println("DESCRIÇÃO: '" + rd.getDescription() + "'");
System.out.println("CATEGORIA: '" + rd.getCategory() + "'");
System.out.println("GRUPO: '" + rd.getCategory() + "'");
String suggestionRuta = problem
.getFeatureValueAsString(mSuggestionFeature);
System.out.println("SUGESTÃO: '" + suggestionRuta + "'");
for (Example example : rd.getExamples())
System.out
.format("EXEMPLO CORRETO: '%s'\nEXEMPLO INCORRETO: '%s'\n",
example.getCorrect(),
example.getIncorrect());
String[] originalTokens = problem.getCoveredText().split(
"(\\s+|-)");
for (String sugPossibility : suggestionRuta.split("\\|")) {
String[] tokens = originalTokens.clone();
for (String sugItem : sugPossibility.split(";")) {
Matcher m;
if ((m = SWAP.matcher(sugItem)).find()) {
int i = Integer.parseInt(m.group(1)) - 1;
int j = Integer.parseInt(m.group(2)) - 1;
String tmp = tokens[i];
tokens[i] = tokens[j];
tokens[j] = tmp;
} else if ((m = REPLACE_LEXEME.matcher(sugItem)).find()) {
int i = Integer.parseInt(m.group(1)) - 1;
String text = m.group(2);
tokens[i] = text;
} else if ((m = REPLACE_MAPPING.matcher(sugItem))
.find()) {
int i = Integer.parseInt(m.group(1)) - 1;
Matcher m2 = REPLACE_MAPPING2.matcher(m.group(2));
while (m2.find()) {
if (m2.group(1).equals(tokens[i])) {
tokens[i] = m2.group(2);
break;
}
}
} else if ((m = REPLACE_TAGR.matcher(sugItem)).find()) {
int i = Integer.parseInt(m.group(1)) - 1;
int j = Integer.parseInt(m.group(2)) - 1;
String tagMaskStr = m.group(3);
TagMask tagMask = TagMaskUtils.parse(tagMaskStr);
tokens[i] = suggestionBuilder.getBestFlexedWord(
coveredTokens.get(j), tagMask);
} else if ((m = REPLACE_R.matcher(sugItem)).find()) {
int i = Integer.parseInt(m.group(3)) - 1;
int j = Integer.parseInt(m.group(4)) - 1;
String tagMaskStr = m.group(1);
TagMask tagMask = TagMaskUtils
.createTagMaskFromToken(
coveredTokens.get(j), tagMaskStr);
tokens[i] = suggestionBuilder.getBestFlexedWord(
coveredTokens.get(i), tagMask);
}
}
StringBuilder s = new StringBuilder(applyCase(tokens[0],
getCase(originalTokens[0])));
for (int k = 1; k < tokens.length; k++) {
s.append(" "
+ applyCase(tokens[k],
getCase(originalTokens[k])));
}
System.out.format("SUGESTÃO: '%s'\n", s);
suggestions.add(s.toString());
}
mistakes.add(createMistake(id,
suggestions.toArray(new String[0]), problem.getBegin(),
problem.getEnd(), sentence.getSentence()));
}
} catch (Exception e) { // TODO: tratar exceptions corretamente
LOGGER.fatal("Exception checking sentence with Ruta. ", e);
}
return mistakes;
}
private String[] createSuggestion(String error) {
String[] array = { error };
return array;
}
private boolean typeSystemInitialized = false;
private synchronized void initTypeSystem(TypeSystem typeSystem)
throws AnalysisEngineProcessException {
if (typeSystemInitialized == true) {
return;
}
mProblemType = AnnotatorUtil.getType(typeSystem,
"cogroo.ruta.Main.PROBLEM");
mIDFeature = AnnotatorUtil.getRequiredFeature(mProblemType, "id",
CAS.TYPE_NAME_STRING);
mSuggestionFeature = AnnotatorUtil.getRequiredFeature(mProblemType,
"suggestion", CAS.TYPE_NAME_STRING);
typeSystemInitialized = true;
}
@Override
public String getIdPrefix() {
return "uima:";
}
@Override
public int getPriority() {
return 100;
}
private List<Token> getTokens(Sentence sentence, int begin, int end) {
List<Token> tokens = new ArrayList<>();
Span span = new Span(begin, end);
for (Token token : sentence.getTokens()) {
if (span.contains(token.getSpan())) {
tokens.add(token);
}
}
return tokens;
}
public static void main(String[] args) throws IllegalArgumentException,
IOException {
ComponentFactory factory = ComponentFactory.create(new Locale("pt",
"BR"));
Analyzer cogroo = factory.createPipe();
GrammarChecker gc = new GrammarChecker(cogroo);
// CheckDocument document = new
// CheckDocument("Quanto à lápis, não entendo. Quanto à computador, não entendo. Refiro-me à trabalhos remunerados. Refiro-me à reuniões extraordinárias. Fomos levados à crer. A uma hora estaremos partindo. As duas horas estaremos partindo. Os ônibus estacionaram a direita do pátio. Os ônibus estacionaram a esquerda do pátio. Em relação as atividades programadas. Com relação as atividades programadas. Devido as cobranças injustas. Enviei os documentos à você. Enviei os documentos à Vossa Excelência. Quanto ao lápis, não entendo. Quanto ao computador, não entendo. Refiro-me aos trabalhos remunerados. Refiro-me às reuniões extraordinárias. Refiro-me a reuniões extraordinárias. Fomos levados a crer. À uma hora estaremos partindo. Daqui a uma hora estaremos partindo. Às duas horas estaremos partindo. Os ônibus estacionaram à direita do pátio. Os ônibus estacionaram à esquerda do pátio. Em relação a segurança dos menores. Em relação à segurança dos menores. Em relação às atividades programadas. Com relação a segurança dos menores. Com relação à segurança dos menores. Com relação às atividades programadas. Devido à cobrança injusta. Devido às cobranças injustas. Devido a cobrança injusta. Enviei os documentos a você. Enviei os documentos a Vossa Excelência.");
CheckDocument document = new CheckDocument(
// "Já fazem dias que não o vejo.");
// "Jamais ocorreu-nos esta ideia; agora está anexo os documentos solicitados.");
// passe o doc pelo pipe
"segue anexos o documento solicitado.");
gc.analyze(document);
}
}