Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/* LanguageTool, a natural language style checker
* Copyright (C) 2017 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.server;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.jetbrains.annotations.NotNull;
import org.languagetool.AnalyzedSentence;
import org.languagetool.AnalyzedTokenReadings;
import org.languagetool.Tag;
import org.languagetool.rules.*;
import org.languagetool.tools.Tools;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.SSLHandshakeException;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
/**
* Extend results by adding rules matches from a different API server.
* @since 4.0
*/
class ResultExtender {
private static final Logger logger = LoggerFactory.getLogger(ResultExtender.class);
private final URL url;
private final int connectTimeoutMillis;
private final ObjectMapper mapper = new ObjectMapper();
ResultExtender(String url, int connectTimeoutMillis) {
this.url = Tools.getUrl(url);
if (connectTimeoutMillis <= 0) {
throw new IllegalArgumentException("connectTimeoutMillis must be > 0: " + connectTimeoutMillis);
}
this.connectTimeoutMillis = connectTimeoutMillis;
}
/**
* Filter {@code extensionMatches} so that only those matches are left that don't cover or touch one of the {@code matches}.
*/
@NotNull
List getFilteredExtensionMatches(List matches, List extensionMatches) {
List filteredExtMatches = new ArrayList<>();
for (RemoteRuleMatch extensionMatch : extensionMatches) {
if (!extensionMatch.isTouchedByOneOf(matches)) {
AnalyzedSentence sentence = new AnalyzedSentence(new AnalyzedTokenReadings[]{});
String catId = extensionMatch.getCategoryId().orElse(Categories.MISC.getId().toString());
HiddenRule hiddenRule = new HiddenRule(catId,
extensionMatch.getCategory().orElse("(unknown)"),
extensionMatch.getLocQualityIssueType().orElse(null),
extensionMatch.getTags(),
extensionMatch.estimatedContextForSureMatch());
RuleMatch hiddenRuleMatch = new RuleMatch(hiddenRule, sentence, extensionMatch.getErrorOffset(),
extensionMatch.getErrorOffset()+extensionMatch.getErrorLength(), "(hidden message)");
filteredExtMatches.add(hiddenRuleMatch);
}
}
return filteredExtMatches;
}
@NotNull
List getExtensionMatches(String plainText, Map params) throws IOException {
HttpURLConnection huc = (HttpURLConnection) url.openConnection();
HttpURLConnection.setFollowRedirects(false);
huc.setConnectTimeout(connectTimeoutMillis);
huc.setReadTimeout(connectTimeoutMillis*2);
// longer texts take longer to check, so increase the timeout:
float factor = plainText.length() / 1000.0f;
if (factor > 1) {
int increasedTimeout = (int)(connectTimeoutMillis * 2 * Math.min(factor, 5));
huc.setReadTimeout(increasedTimeout);
}
huc.setRequestMethod("POST");
huc.setDoOutput(true);
try {
huc.connect();
try (DataOutputStream wr = new DataOutputStream(huc.getOutputStream())) {
String urlParameters = "";
List ignoredParameters = Arrays.asList("enableHiddenRules", "username", "password", "token", "apiKey", "c");
for (Map.Entry entry : params.entrySet()) {
// We could set 'language' to the language already detected, so the queried server
// wouldn't need to guess the language again. But then we'd run into cases where
// we get an error because e.g. 'noopLanguages' can only be used with 'language=auto'
if (!ignoredParameters.contains(entry.getKey())) {
urlParameters += "&" + encode(entry.getKey()) + "=" + encode(entry.getValue());
}
}
wr.write(urlParameters.getBytes(StandardCharsets.UTF_8));
}
InputStream input = huc.getInputStream();
return parseJson(input);
} catch (SSLHandshakeException | SocketTimeoutException e) {
// "hard" errors that will probably not resolve themselves easily:
logger.error("Error while querying hidden matches server", e);
throw e;
} catch (Exception e) {
// These are issue that can be request-specific, like wrong parameters. We don't throw an
// exception, as the calling code would otherwise assume this is a persistent error:
logger.warn("Warn: Failed to query hidden matches server at " + url + ": " + e.getClass() + ": " + e.getMessage() + ", input was " + plainText.length() + " characters - request-specific error, ignoring");
return Collections.emptyList();
} finally {
huc.disconnect();
}
}
private String encode(String plainText) throws UnsupportedEncodingException {
return URLEncoder.encode(plainText, StandardCharsets.UTF_8.name());
}
@NotNull
private List parseJson(InputStream inputStream) throws IOException {
Map map = mapper.readValue(inputStream, Map.class);
List matches = (ArrayList) map.get("matches");
List result = new ArrayList<>();
for (Object match : matches) {
RemoteRuleMatch remoteMatch = getMatch((Map)match);
result.add(remoteMatch);
}
return result;
}
@NotNull
private RemoteRuleMatch getMatch(Map match) {
Map rule = (Map) match.get("rule");
int offset = (int) getRequired(match, "offset");
int errorLength = (int) getRequired(match, "length");
Map context = (Map) match.get("context");
int contextOffset = (int) getRequired(context, "offset");
int contextForSureMatch = match.get("contextForSureMatch") != null ? (int) match.get("contextForSureMatch") : 0;
RemoteRuleMatch remoteMatch = new RemoteRuleMatch(getRequiredString(rule, "id"), getRequiredString(match, "message"),
getRequiredString(context, "text"), contextOffset, offset, errorLength, contextForSureMatch);
remoteMatch.setShortMsg(getOrNull(match, "shortMessage"));
remoteMatch.setRuleSubId(getOrNull(rule, "subId"));
remoteMatch.setLocQualityIssueType(getOrNull(rule, "issueType"));
List tags = getTagList(rule, "tags");
if (tags.size() > 0) {
List tagsObjects = new ArrayList<>();
for (String tag : tags) {
tagsObjects.add(Tag.valueOf(tag));
}
remoteMatch.setTags(tagsObjects);
}
List urls = getValueList(rule, "urls");
if (urls.size() > 0) {
remoteMatch.setUrl(urls.get(0));
}
Map category = (Map) rule.get("category");
remoteMatch.setCategory(getOrNull(category, "name"));
remoteMatch.setCategoryId(getOrNull(category, "id"));
remoteMatch.setReplacements(getValueList(match, "replacements"));
return remoteMatch;
}
private Object getRequired(Map elem, String propertyName) {
Object val = elem.get(propertyName);
if (val != null) {
return val;
}
throw new RuntimeException("JSON item " + elem + " doesn't contain required property '" + propertyName + "'");
}
private String getRequiredString(Map elem, String propertyName) {
return (String) getRequired(elem, propertyName);
}
private String getOrNull(Map elem, String propertyName) {
Object val = elem.get(propertyName);
if (val != null) {
return (String) val;
}
return null;
}
private List getValueList(Map match, String propertyName) {
List