org.languagetool.dev.wikipedia.SuggestionReplacer.java Source code

Java tutorial

Introduction

Here is the source code for org.languagetool.dev.wikipedia.SuggestionReplacer.java

Source

/* LanguageTool, a natural language style checker
 * Copyright (C) 2013 Daniel Naber (http://www.danielnaber.de)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
 * USA
 */
package org.languagetool.dev.wikipedia;

import org.apache.commons.lang3.StringUtils;
import org.languagetool.rules.RuleMatch;
import xtc.tree.Location;

import java.util.ArrayList;
import java.util.List;

/**
 * Applies the rule suggestions to a text, considering a mapping so the
 * suggestion can be applied to the original markup of a text.
 */
public class SuggestionReplacer {

    private final PlainTextMapping textMapping;
    private final String originalText;
    private final ErrorMarker errorMarker;

    /**
     * @param originalText the original text that includes markup
     */
    public SuggestionReplacer(PlainTextMapping textMapping, String originalText) {
        // use <<span>> to avoid clashes with <span> in original markup:
        this(textMapping, originalText, new ErrorMarker("<<span class=\"error\">>", "<</span>>"));
    }

    /**
     * @since 2.6
     */
    public SuggestionReplacer(PlainTextMapping textMapping, String originalText, ErrorMarker errorMarker) {
        this.textMapping = textMapping;
        this.originalText = originalText;
        this.errorMarker = errorMarker;
    }

    /**
     * Applies the suggestions from the rule to the original text. For rules that
     * have no suggestion, a pseudo-correction is generated that contains the same
     * text as before.
     */
    public List<RuleMatchApplication> applySuggestionsToOriginalText(RuleMatch match) {
        List<String> replacements = new ArrayList<>(match.getSuggestedReplacements());
        boolean hasRealReplacements = replacements.size() > 0;
        if (!hasRealReplacements) {
            // create a pseudo replacement with the error text itself
            String plainText = textMapping.getPlainText();
            replacements.add(plainText.substring(match.getFromPos(), match.getToPos()));
        }

        List<RuleMatchApplication> ruleMatchApplications = new ArrayList<>();
        Location fromPosLocation = textMapping.getOriginalTextPositionFor(match.getFromPos() + 1); // not zero-based!
        Location toPosLocation = textMapping.getOriginalTextPositionFor(match.getToPos() + 1);

        /*System.out.println("=========");
        System.out.println(textMapping.getMapping());
        System.out.println("=========");
        System.out.println(textMapping.getPlainText());
        System.out.println("=========");
        System.out.println(originalText);
        System.out.println("=========");*/

        int fromPos = LocationHelper.absolutePositionFor(fromPosLocation, originalText);
        int toPos = LocationHelper.absolutePositionFor(toPosLocation, originalText);
        for (String replacement : replacements) {
            String errorText = textMapping.getPlainText().substring(match.getFromPos(), match.getToPos());
            // the algorithm is off a bit sometimes due to the complex syntax, so consider the next whitespace:
            int contextFrom = findNextWhitespaceToTheLeft(originalText, fromPos);
            int contextTo = findNextWhitespaceToTheRight(originalText, toPos);

            /*System.out.println(match + ":");
            System.out.println("match.getFrom/ToPos(): " + match.getFromPos() + "/" + match.getToPos());
            System.out.println("from/toPosLocation: " + fromPosLocation + "/" + toPosLocation);
            System.out.println("from/toPos: " + fromPos + "/" + toPos);
            System.out.println("contextFrom/To: " + contextFrom + "/" + contextTo);*/

            String context = originalText.substring(contextFrom, contextTo);
            String text = originalText.substring(0, contextFrom) + errorMarker.getStartMarker() + context
                    + errorMarker.getEndMarker() + originalText.substring(contextTo);
            String newContext;
            if (StringUtils.countMatches(context, errorText) == 1) {
                newContext = context.replace(errorText, replacement);
            } else {
                // This may happen especially for very short strings. As this is an
                // error, we don't claim to have real replacements:
                newContext = context;
                hasRealReplacements = false;
            }
            String newText = originalText.substring(0, contextFrom)
                    // we do a simple string replacement as that works even if our mapping is off a bit:
                    + errorMarker.getStartMarker() + newContext + errorMarker.getEndMarker()
                    + originalText.substring(contextTo);
            RuleMatchApplication application;
            if (hasRealReplacements) {
                application = RuleMatchApplication.forMatchWithReplacement(match, text, newText, errorMarker);
            } else {
                application = RuleMatchApplication.forMatchWithoutReplacement(match, text, newText, errorMarker);
            }
            ruleMatchApplications.add(application);
        }
        return ruleMatchApplications;
    }

    int findNextWhitespaceToTheRight(String text, int position) {
        for (int i = position; i < text.length(); i++) {
            if (Character.isWhitespace(text.charAt(i))) {
                return i;
            }
        }
        return text.length();
    }

    int findNextWhitespaceToTheLeft(String text, int position) {
        for (int i = position; i >= 0; i--) {
            if (Character.isWhitespace(text.charAt(i))) {
                return i + 1;
            }
        }
        return 0;
    }

}