diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2853844..381850b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+1.4.0
+--
+част. снятие омонимии на морф. анализе; испр. нач.формы глагола; подд. е вместо ё; расширение словаря
+
1.2.16
--
diff --git a/pom.xml b/pom.xml
index 34d4965..c022733 100644
--- a/pom.xml
+++ b/pom.xml
@@ -12,9 +12,13 @@
ru.textanalysis.tawt
tawt-parent
- 0.0.5
+ 0.1.0
+
+ 1.14.2
+
+
@@ -49,6 +53,13 @@
lombok
provided
+
+
+
+ org.jsoup
+ jsoup
+ ${jsoup.version}
+
diff --git a/src/main/java/ru/textanalysis/tawt/ms/constant/Const.java b/src/main/java/ru/textanalysis/tawt/ms/constant/Const.java
index 2ec5d28..b936106 100644
--- a/src/main/java/ru/textanalysis/tawt/ms/constant/Const.java
+++ b/src/main/java/ru/textanalysis/tawt/ms/constant/Const.java
@@ -6,6 +6,10 @@ public interface Const {
int BUFFER_SIZE_FOR_INSERT = 10_000;
String START_INSERT = "INSERT INTO 'Form' ('id','StringForm') VALUES ";
String CONTINUED_INSERT = "(%d, '%s')";
+
+ String TAB_SEPARATOR = "\t";
String COMMA_SEPARATOR = ",";
String SEMICOLON_SEPARATOR = ";";
+
+ String TAB_AND_COMMA_REGEX = "[,;]";
}
diff --git a/src/main/java/ru/textanalysis/tawt/ms/constant/TypeOfSpeechs.java b/src/main/java/ru/textanalysis/tawt/ms/constant/TypeOfSpeechs.java
new file mode 100644
index 0000000..8276221
--- /dev/null
+++ b/src/main/java/ru/textanalysis/tawt/ms/constant/TypeOfSpeechs.java
@@ -0,0 +1,12 @@
+package ru.textanalysis.tawt.ms.constant;
+
+public interface TypeOfSpeechs {
+
+ String VERB = "VERB";
+ String NUMR = "NUMR";
+ String ADJF = "ADJF";
+ String ADJS = "ADJS";
+ String PRTF = "PRTF";
+ String PRTS = "PRTS";
+ String INFN = "INFN";
+}
diff --git a/src/main/java/ru/textanalysis/tawt/ms/conversion/dictionary/ConversionDictionary.java b/src/main/java/ru/textanalysis/tawt/ms/conversion/dictionary/ConversionDictionary.java
index a49aac6..3b031fa 100644
--- a/src/main/java/ru/textanalysis/tawt/ms/conversion/dictionary/ConversionDictionary.java
+++ b/src/main/java/ru/textanalysis/tawt/ms/conversion/dictionary/ConversionDictionary.java
@@ -38,22 +38,24 @@
package ru.textanalysis.tawt.ms.conversion.dictionary;
import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang3.StringUtils;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
import ru.textanalysis.tawt.ms.loader.DatabaseFactory;
import ru.textanalysis.tawt.ms.loader.DatabaseLemmas;
import ru.textanalysis.tawt.ms.loader.DatabaseStrings;
-import java.io.BufferedReader;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import java.io.File;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Locale;
-import java.util.regex.Pattern;
+import java.util.*;
+
+import static ru.textanalysis.tawt.ms.constant.Const.COMMA_SEPARATOR;
+import static ru.textanalysis.tawt.ms.constant.Const.TAB_SEPARATOR;
+import static ru.textanalysis.tawt.ms.constant.TypeOfSpeechs.INFN;
+import static ru.textanalysis.tawt.ms.constant.TypeOfSpeechs.VERB;
@Slf4j
public class ConversionDictionary {
@@ -63,12 +65,11 @@ public class ConversionDictionary {
public static void main(String[] args) {
ConversionDictionary conversionDictionary = new ConversionDictionary();
- conversionDictionary.conversionDictionary("dict.opcorpora.txt", StandardCharsets.UTF_8);
+ conversionDictionary.conversionDictionary("dict.opcorpora.xml", StandardCharsets.UTF_8);
}
- // todo: йо
- public void conversionDictionary(String sourceDictionaryPath, Charset encoding) {
- List> lemmas = convertLemmasFromInitDictionary(sourceDictionaryPath, encoding);
+ public void conversionDictionary(String sourceDictionaryPath, Charset encoding, String... additionalDictionaryPaths) {
+ List> lemmas = convertLemmasFromInitDictionary(sourceDictionaryPath, encoding, additionalDictionaryPaths);
databaseLemmas.recreate(lemmas);
databaseStrings.recreate(lemmas);
@@ -76,38 +77,166 @@ public void conversionDictionary(String sourceDictionaryPath, Charset encoding)
databaseLemmas.compression();
}
- private List> convertLemmasFromInitDictionary(String sourceDictionaryPath, Charset encoding) {
+ private List> convertLemmasFromInitDictionary(String sourceDictionaryPath, Charset encoding, String... additionalDictionaryPaths) {
+ List dictionaryPaths = new ArrayList<>();
+ dictionaryPaths.add(sourceDictionaryPath);
+ dictionaryPaths.addAll(Arrays.asList(additionalDictionaryPaths));
List> lemmas = new ArrayList<>();
- try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(sourceDictionaryPath), encoding))) {
- while (bufferedReader.ready()) {
- String initForm = bufferedReader.readLine();
- if (StringUtils.isNotBlank(initForm) && !Pattern.matches("\\d+", initForm)) { //todo
- List lemma = new LinkedList<>();
- FormForConversion initialForm = createForm(initForm, true);
- lemma.add(initialForm);
- while (bufferedReader.ready()) {
- String derivativeForm = bufferedReader.readLine();
- if (StringUtils.isBlank(derivativeForm)) {
- break;
+ HashMap> lemmasMap = new HashMap<>();
+ HashMap> verbs = new HashMap<>();
+
+ try {
+ DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+ for (String dictionaryPath : dictionaryPaths) {
+ Document document = documentBuilder.parse(new File(dictionaryPath).toURI().toString());
+
+ Node dictionary = document.getDocumentElement();
+ NodeList dictionaryProps = dictionary.getChildNodes();
+ for (int i = 0; i < dictionaryProps.getLength(); i++) {
+ Node node = dictionaryProps.item(i);
+ if (node.getNodeType() != Node.TEXT_NODE && (node.getNodeName().equals("grammemes") || node.getNodeName().equals("restrictions") || node.getNodeName().equals("link_types"))) {
+ while (node.hasChildNodes()) {
+ node.removeChild(node.getFirstChild());
+ }
+ }
+ }
+ for (int i = 0; i < dictionaryProps.getLength(); i++) {
+ Node lemmata = dictionaryProps.item(i);
+ if (lemmata.getNodeType() != Node.TEXT_NODE && lemmata.getNodeName().equals("lemmata")) {
+ NodeList lemmataProps = lemmata.getChildNodes();
+ for (int j = 0; j < lemmataProps.getLength(); j++) {
+ Node lemma = lemmataProps.item(j);
+ if (lemma.getNodeType() != Node.TEXT_NODE && lemma.getNodeName().equals("lemma")) {
+ String commonCharacteristics = "";
+ int formNumber = 0;
+ boolean isVerb = false;
+ List verbInfn = new ArrayList<>();
+ List wordLemma = new LinkedList<>();
+ NodeList lemmaProps = lemma.getChildNodes();
+ for (int k = 0; k < lemmaProps.getLength(); k++) {
+ Node value = lemmaProps.item(k);
+ if (value.getNodeType() != Node.TEXT_NODE && value.getNodeName().equals("l")) {
+ NodeList valueProps = value.getChildNodes();
+ for (int m = 0; m < valueProps.getLength(); m++) {
+ Node characteristic = valueProps.item(m);
+ if (characteristic.getNodeType() != Node.TEXT_NODE && characteristic.getNodeName().equals("g")) {
+ commonCharacteristics += characteristic.getAttributes().getNamedItem("v").getNodeValue();
+ commonCharacteristics += COMMA_SEPARATOR;
+ }
+ }
+ if (commonCharacteristics.length() > 0) {
+ commonCharacteristics = commonCharacteristics.substring(0, commonCharacteristics.length() - 1);
+ }
+ } else if (value.getNodeType() != Node.TEXT_NODE && value.getNodeName().equals("f")) {
+ StringBuilder formCharacteristics = new StringBuilder(value.getAttributes().getNamedItem("t").getNodeValue());
+ formCharacteristics.append(TAB_SEPARATOR);
+ formCharacteristics.append(commonCharacteristics);
+ NodeList valueProps = value.getChildNodes();
+ for (int m = 0; m < valueProps.getLength(); m++) {
+ Node characteristic = valueProps.item(m);
+ if (characteristic.getNodeType() != Node.TEXT_NODE && characteristic.getNodeName().equals("g")) {
+ formCharacteristics.append(COMMA_SEPARATOR);
+ formCharacteristics.append(characteristic.getAttributes().getNamedItem("v").getNodeValue());
+ }
+ }
+ if (formCharacteristics.toString().contains(INFN) || formCharacteristics.toString().contains(VERB)) {
+ isVerb = true;
+ verbInfn.add(formCharacteristics.toString());
+ } else {
+ if (formNumber == 0) {
+ FormForConversion initialForm = createForm(formCharacteristics.toString(), true);
+ wordLemma.add(initialForm);
+ } else {
+ wordLemma.add(createForm(formCharacteristics.toString(), false));
+ }
+ formNumber++;
+ }
+ }
+ }
+ if (isVerb) {
+ verbs.put(Integer.valueOf(lemma.getAttributes().getNamedItem("id").getNodeValue()), verbInfn);
+ } else {
+ lemmasMap.put(Integer.valueOf(lemma.getAttributes().getNamedItem("id").getNodeValue()), wordLemma);
+ }
+ while (lemma.hasChildNodes()) {
+ lemma.removeChild(lemma.getFirstChild());
+ }
+ }
+ }
+ } else if (lemmata.getNodeType() != Node.TEXT_NODE && lemmata.getNodeName().equals("links")) {
+ NodeList lemmataProps = lemmata.getChildNodes();
+ for (int j = 0; j < lemmataProps.getLength(); j++) {
+ Node lemma = lemmataProps.item(j);
+ if (lemma.getNodeType() != Node.TEXT_NODE && lemma.getNodeName().equals("link")) {
+ if (Objects.equals(lemma.getAttributes().getNamedItem("type").getNodeValue(), "3")) {
+ List wordLemma = new LinkedList<>();
+ List infn = verbs.get(Integer.parseInt(lemma.getAttributes().getNamedItem("from").getNodeValue()));
+ FormForConversion initialForm = createForm(infn.get(0).replaceAll(INFN, VERB), true);
+ wordLemma.add(initialForm);
+ List verb = verbs.get(Integer.parseInt(lemma.getAttributes().getNamedItem("to").getNodeValue()));
+ verb.forEach(form -> {
+ FormForConversion derivativeForm = createForm(form, false);
+ wordLemma.add(derivativeForm);
+ });
+ lemmasMap.put(Integer.valueOf(lemma.getAttributes().getNamedItem("from").getNodeValue()), wordLemma);
+ } else if (!Objects.equals(lemma.getAttributes().getNamedItem("type").getNodeValue(), "11")) {
+ if (lemmasMap.containsKey(Integer.parseInt(lemma.getAttributes().getNamedItem("to").getNodeValue()))) {
+ lemmasMap.get(Integer.parseInt(lemma.getAttributes().getNamedItem("to").getNodeValue())).forEach(formLemma -> {
+ if (lemmasMap.containsKey(Integer.parseInt(lemma.getAttributes().getNamedItem("from").getNodeValue()))) {
+ formLemma.setLink(lemmasMap.get(Integer.parseInt(lemma.getAttributes().getNamedItem("from").getNodeValue())).get(0).hashCode(),
+ lemmasMap.get(Integer.parseInt(lemma.getAttributes().getNamedItem("from").getNodeValue())).get(0).getKey());
+ }
+ });
+ }
+ }
+ while (lemma.hasChildNodes()) {
+ lemma.removeChild(lemma.getFirstChild());
+ }
+ }
}
- lemma.add(createForm(derivativeForm, false));
}
- lemmas.add(lemma);
}
}
- } catch (IOException ex) {
+ lemmas = new ArrayList<>(lemmasMap.values());
+ lemmasMap.clear();
+ verbs.clear();
+ lemmas.add(addSupportYo(lemmas));
+ } catch (Exception ex) {
log.error("Ошибка при чтении файла. Файл: {}", sourceDictionaryPath, ex);
}
return lemmas;
}
+ private List addSupportYo(List> lemmas) {
+ boolean firstYo = true;
+ List yoLemma = new LinkedList<>();
+ for (List lemma : lemmas) {
+ for (FormForConversion formForConversion : lemma) {
+ if (formForConversion.getStringName().contains("ё")) {
+ String curLemmaString = formForConversion.getStringName().split(TAB_SEPARATOR)[0];
+ if (firstYo) {
+ FormForConversion yoForm = createForm(curLemmaString.replaceAll("ё", "е"), true);
+ yoForm.setLink(formForConversion.hashCode(), formForConversion.getKey());
+ yoLemma.add(yoForm);
+ firstYo = false;
+ } else {
+ FormForConversion yoForm = createForm(curLemmaString.replaceAll("ё", "е"), false);
+ yoForm.setLink(formForConversion.hashCode(), formForConversion.getKey());
+ yoLemma.add(yoForm);
+ }
+ }
+ }
+ }
+ return yoLemma;
+ }
+
private FormForConversion createForm(String line, boolean isInitialForm) {
- String[] parameters = line.toLowerCase(Locale.ROOT).split("\t");
+ String[] parameters = line.toLowerCase(Locale.ROOT).split(TAB_SEPARATOR);
FormForConversion form = new FormForConversion(parameters[0], isInitialForm);
if (parameters.length > 1) {
form.setCharacteristics(parameters[1].split("[, ]"));
} else {
- System.out.println("error"); //todo
+ form.setCharacteristics(new String[0]);
}
return form;
}
diff --git a/src/main/java/ru/textanalysis/tawt/ms/conversion/dictionary/FormForConversion.java b/src/main/java/ru/textanalysis/tawt/ms/conversion/dictionary/FormForConversion.java
index 1b81246..1fedae6 100644
--- a/src/main/java/ru/textanalysis/tawt/ms/conversion/dictionary/FormForConversion.java
+++ b/src/main/java/ru/textanalysis/tawt/ms/conversion/dictionary/FormForConversion.java
@@ -23,11 +23,14 @@ public class FormForConversion {
private final int key;
private byte partOfSpeech;
private byte[] morfCharacteristics;
+ private byte[] Link;
private boolean isFirstKey;
protected FormForConversion(String stringName, boolean isInitialForm) {
this.stringName = stringName.toLowerCase(Locale.ROOT);
key = createKey(isInitialForm);
+ long link = 0;
+ this.Link = getBytes(link);
}
public String getStringName() {
@@ -35,15 +38,28 @@ public String getStringName() {
}
protected void setCharacteristics(String[] characteristics) {
- List parameters = new ArrayList<>(Arrays.asList(characteristics));
- setPartOfSpeech(conversionPartOfSpeech(parameters));
- setMorfCharacteristics(getBytes(conversionMorfCharacteristics(parameters)));
+ if (characteristics.length > 0) {
+ List parameters = new ArrayList<>(Arrays.asList(characteristics));
+ setPartOfSpeech(conversionPartOfSpeech(parameters));
+ setMorfCharacteristics(getBytes(conversionMorfCharacteristics(parameters)));
+ } else {
+ setPartOfSpeech((byte) 0);
+ long chars = 0;
+ setMorfCharacteristics(getBytes(chars));
+ }
}
private void setPartOfSpeech(byte partOfSpeech) {
this.partOfSpeech = partOfSpeech;
}
+ protected void setLink(int hash, int key) {
+ long morphLink = hash;
+ morphLink = morphLink << 32;
+ morphLink += key;
+ this.Link = getBytes(morphLink);
+ }
+
private byte getPartOfSpeech() {
return partOfSpeech;
}
@@ -114,6 +130,10 @@ private byte[] getMorfCharacteristics() {
return morfCharacteristics;
}
+ private byte[] getLink() {
+ return Link;
+ }
+
public int getKey() {
return key;
}
@@ -157,6 +177,7 @@ public byte[] getByteFileFormat() {
bytesFormat = plusByte(bytesFormat, getPartOfSpeech());
}
bytesFormat = plusByte(bytesFormat, getMorfCharacteristics());
+ bytesFormat = plusByte(bytesFormat, getLink());
return bytesFormat;
}
diff --git a/src/main/java/ru/textanalysis/tawt/ms/dictionary/convertor/WiktionaryTagsData.java b/src/main/java/ru/textanalysis/tawt/ms/dictionary/convertor/WiktionaryTagsData.java
new file mode 100644
index 0000000..fc85fa7
--- /dev/null
+++ b/src/main/java/ru/textanalysis/tawt/ms/dictionary/convertor/WiktionaryTagsData.java
@@ -0,0 +1,118 @@
+package ru.textanalysis.tawt.ms.dictionary.convertor;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static ru.textanalysis.tawt.ms.constant.TypeOfSpeechs.*;
+
+/**
+ * Хранение преобразованания тегов с Wiktionary в стандарт OpenCorpora
+ */
+public class WiktionaryTagsData {
+
+ private final Map tagsDictionary;
+ private final List toSDictionary;
+
+ public WiktionaryTagsData() {
+ tagsDictionary = new HashMap<>();
+ tagsDictionary.put("прилагательное", ADJF);
+ tagsDictionary.put("качественное", "Qual");
+ tagsDictionary.put("ед. ч.", "sing");
+ tagsDictionary.put("мн. ч.", "plur");
+ tagsDictionary.put("муж. р.", "masc");
+ tagsDictionary.put("ср. р.", "neut");
+ tagsDictionary.put("жен. р.", "femn");
+ tagsDictionary.put("существительное", "NOUN");
+ tagsDictionary.put("притяжательное прилагательное", "ADJF,Poss");
+ tagsDictionary.put("прилагательное (притяжательное)", "ADJF,Poss");
+ tagsDictionary.put("прилагательное (относительное)", ADJF);
+ tagsDictionary.put("относительное прилагательное", ADJF);
+ tagsDictionary.put("местоименное прилагательное", "ADJF,Apro");
+ tagsDictionary.put("притяжательное местоимение (местоименное прилагательное)", "NPRO,Poss");
+ tagsDictionary.put("определительное местоимение (местоименное прилагательное)", "ADJF,Apro");
+ tagsDictionary.put("общий род (может согласовываться с другими частями речи как мужского, так и женского рода)", "ms-f");
+ tagsDictionary.put("им.", "nomn");
+ tagsDictionary.put("р.", "gent");
+ tagsDictionary.put("д.", "datv");
+ tagsDictionary.put("в.", "accs");
+ tagsDictionary.put("т.", "ablt");
+ tagsDictionary.put("тв.", "ablt");
+ tagsDictionary.put("п.", "loct");
+ tagsDictionary.put("пр.", "loct");
+ tagsDictionary.put("одуш.", "anim");
+ tagsDictionary.put("неод.", "inan");
+ tagsDictionary.put("глагол", VERB);
+ tagsDictionary.put("одушевлённое", "anim");
+ tagsDictionary.put("неодушевлённое", "inan");
+ tagsDictionary.put("совершенный вид", "perf");
+ tagsDictionary.put("переходный", "tran");
+ tagsDictionary.put("несовершенный вид", "impf");
+ tagsDictionary.put("непереходный", "intr");
+ tagsDictionary.put("будущ.", "futr");
+ tagsDictionary.put("прош.", "past");
+ tagsDictionary.put("наст.", "pres");
+ tagsDictionary.put("повелит.", "impr");
+ tagsDictionary.put("средний род", "neut");
+ tagsDictionary.put("мужской род", "masc");
+ tagsDictionary.put("женский род", "femn");
+ tagsDictionary.put("действительное причастие", "PRTF,actv");
+ tagsDictionary.put("страдательное причастие", "PRTF,pssv");
+ tagsDictionary.put("совершенного вида", "perf");
+ tagsDictionary.put("несовершенного вида", "impf");
+ tagsDictionary.put("прошедшего времени", "past");
+ tagsDictionary.put("настоящего времени", "pres");
+ tagsDictionary.put("будущего времени", "futr");
+ tagsDictionary.put("невозвратное деепричастие", "GRND");
+ tagsDictionary.put("возвратное деепричастие", "GRND,Refl");
+ tagsDictionary.put("возвратный", "Refl");
+ tagsDictionary.put("возвратное причастие", "PRTF,Refl");
+ tagsDictionary.put("числительное", NUMR);
+ tagsDictionary.put("порядковое", "Anum");
+ tagsDictionary.put("вводное слово", "CONJ,Prnt");
+ tagsDictionary.put("также вводное слово", "Prnt");
+ tagsDictionary.put("наречие", "ADVB");
+ tagsDictionary.put("предлог", "PREP");
+ tagsDictionary.put("союз", "CONJ");
+ tagsDictionary.put("частица", "PRCL");
+ tagsDictionary.put("междометие", "INTJ");
+ tagsDictionary.put("я", "1per");
+ tagsDictionary.put("мы", "1per");
+ tagsDictionary.put("ты", "2per");
+ tagsDictionary.put("вы", "2per");
+ tagsDictionary.put("он она оно", "3per");
+ tagsDictionary.put("они", "3per");
+ tagsDictionary.put("местоимение (притяжательное)", "ADJF,Apro");
+ tagsDictionary.put("предикатив", "PRED");
+ tagsDictionary.put("порядковое числительное (счётное прилагательное)", "ADJF,Anum");
+ tagsDictionary.put("порядковое числительное", "ADJF,Anum");
+ tagsDictionary.put("счётное прилагательное", "ADJF,Anum");
+
+ toSDictionary = new ArrayList<>();
+ toSDictionary.add(ADJF);
+ toSDictionary.add(ADJS);
+ toSDictionary.add("NOUN");
+ toSDictionary.add("PRED");
+ toSDictionary.add("INTJ");
+ toSDictionary.add("PRCL");
+ toSDictionary.add("CONJ");
+ toSDictionary.add("PREP");
+ toSDictionary.add("ADVB");
+ toSDictionary.add(NUMR);
+ toSDictionary.add(PRTF);
+ toSDictionary.add(PRTS);
+ toSDictionary.add("GRND");
+ toSDictionary.add(VERB);
+ toSDictionary.add(INFN);
+ toSDictionary.add("NPRO");
+ }
+
+ public Map getTags() {
+ return tagsDictionary;
+ }
+
+ public List getToS() {
+ return toSDictionary;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/textanalysis/tawt/ms/dictionary/convertor/WiktionaryToOpenCorporaConverter.java b/src/main/java/ru/textanalysis/tawt/ms/dictionary/convertor/WiktionaryToOpenCorporaConverter.java
new file mode 100644
index 0000000..dce6849
--- /dev/null
+++ b/src/main/java/ru/textanalysis/tawt/ms/dictionary/convertor/WiktionaryToOpenCorporaConverter.java
@@ -0,0 +1,80 @@
+package ru.textanalysis.tawt.ms.dictionary.convertor;
+
+import static ru.textanalysis.tawt.ms.constant.Const.*;
+import static ru.textanalysis.tawt.ms.constant.TypeOfSpeechs.*;
+
+/**
+ * Преобразование тегов с Wiktionary в стандарт OpenCorpora
+ */
+public class WiktionaryToOpenCorporaConverter {
+
+ private final WiktionaryTagsData wiktionaryTagsData;
+
+ /**
+ * Instantiates a new Tags to word form converter.
+ *
+ * @param wiktionaryTagsData объект с информацией о преобразовании тегов
+ */
+ public WiktionaryToOpenCorporaConverter(WiktionaryTagsData wiktionaryTagsData) {
+ this.wiktionaryTagsData = wiktionaryTagsData;
+ }
+
+ /**
+ * Преобразовывает неизменяемые части речи из
+ * стандарта Wiktionary в стандарт OpenCorpora
+ *
+ * @param token слово и его теги, разделённые '\t'
+ * @return WordForm с леммой в стандарте OpenCorpora
+ */
+ public WordFormForConverter convertImmutableToWordForm(String token) {
+ String[] tokens = token.split(TAB_SEPARATOR);
+ String[] tags = tokens[1].split(TAB_AND_COMMA_REGEX);
+ StringBuilder builder = new StringBuilder(tokens[0]).append(TAB_SEPARATOR);
+ for (String tag : tags) {
+ tag = tag.trim();
+ if (wiktionaryTagsData.getTags().containsKey(tag)) {
+ builder.append(wiktionaryTagsData.getTags().get(tag)).append(COMMA_SEPARATOR);
+ }
+ }
+ String result = builder.toString();
+ if (result.contains(COMMA_SEPARATOR)) {
+ result = result.substring(0, result.length() - 1).replaceAll(VERB, INFN);
+ }
+ return new WordFormForConverter(result);
+ }
+
+ /**
+ * Преобразовывает изменяемые части речи из
+ * стандарте Wiktionary в стандарт OpenCorpora
+ * Задействуется таблица словоформ
+ *
+ * @param token слово и его теги, разделённые '\t'
+ * @return WordForm с леммой в стандарте OpenCorpora
+ */
+ public WordFormForConverter convertTableFormToWordForm(String token) {
+ String[] tokens = token.split(TAB_SEPARATOR);
+ String[] tags = tokens[1].split(TAB_AND_COMMA_REGEX);
+ StringBuilder builder = new StringBuilder(tokens[0]).append(TAB_SEPARATOR);
+ for (String tag : tags) {
+ tag = tag.trim();
+ if (wiktionaryTagsData.getTags().containsKey(tag) && builder.indexOf(wiktionaryTagsData.getTags().get(tag)) == -1) {
+ builder.append(wiktionaryTagsData.getTags().get(tag)).append(COMMA_SEPARATOR);
+ }
+ }
+ String result = builder.toString();
+ if (result.contains(COMMA_SEPARATOR)) {
+ result = result.substring(0, result.length() - 1);
+ }
+ if (token.contains("порядковое")) {
+ result = result.replaceAll(NUMR, ADJF);
+ }
+ if (token.contains("кратк. форма")) {
+ if (result.contains(ADJF)) {
+ result = result.replaceAll(ADJF, ADJS);
+ } else if (result.contains(PRTF)) {
+ result = result.replaceAll(PRTF, PRTS);
+ }
+ }
+ return new WordFormForConverter(result);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/textanalysis/tawt/ms/dictionary/convertor/WordFormForConverter.java b/src/main/java/ru/textanalysis/tawt/ms/dictionary/convertor/WordFormForConverter.java
new file mode 100644
index 0000000..7fd7344
--- /dev/null
+++ b/src/main/java/ru/textanalysis/tawt/ms/dictionary/convertor/WordFormForConverter.java
@@ -0,0 +1,77 @@
+package ru.textanalysis.tawt.ms.dictionary.convertor;
+
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static ru.textanalysis.tawt.ms.constant.Const.COMMA_SEPARATOR;
+import static ru.textanalysis.tawt.ms.constant.Const.TAB_SEPARATOR;
+
+/**
+ * Хранение слова и его тегов
+ */
+@Slf4j
+public class WordFormForConverter {
+
+ private String word;
+ private List tags;
+
+ /**
+ * Instantiates a new Word form.
+ *
+ * @param OpenCorporaWordForm слово и его теги в стандарте OpenCorpora
+ */
+ public WordFormForConverter(String OpenCorporaWordForm) {
+ try {
+ if (OpenCorporaWordForm.length() == 0) {
+ throw new Exception("Неверный формат словоформы");
+ }
+ String[] wordTags = OpenCorporaWordForm.split(TAB_SEPARATOR);
+ if (wordTags.length != 2) {
+ throw new Exception("Неверный формат словоформы");
+ }
+ this.word = wordTags[0];
+ String[] tags = wordTags[1].split(COMMA_SEPARATOR);
+ this.tags = new ArrayList<>();
+ this.tags.addAll(Arrays.asList(tags));
+ } catch (Exception ex) {
+ log.info(ex.getMessage(), ex);
+ }
+ }
+
+ public String getWord() {
+ return word;
+ }
+
+ public List getTags() {
+ return tags;
+ }
+
+ /**
+ * проверка, содержится в WordForm необходимый тег
+ *
+ * @param str проверяемое значение
+ *
+ * @return the boolean
+ */
+ public boolean contains(String str) {
+ return this.toString().contains(str);
+ }
+
+ @Override
+ public String toString() {
+ try {
+ StringBuilder result = new StringBuilder(this.word + TAB_SEPARATOR + this.tags.get(0));
+ for (int i = 1; i < this.tags.size(); i++) {
+ result.append(COMMA_SEPARATOR);
+ result.append(this.tags.get(i));
+ }
+ return result.toString();
+ } catch (Exception ex) {
+ log.error("Не удалось получить токен.", ex);
+ return "";
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/textanalysis/tawt/ms/dictionary/open/corpora/OpenCorporaXmlDocument.java b/src/main/java/ru/textanalysis/tawt/ms/dictionary/open/corpora/OpenCorporaXmlDocument.java
new file mode 100644
index 0000000..ae4b1f9
--- /dev/null
+++ b/src/main/java/ru/textanalysis/tawt/ms/dictionary/open/corpora/OpenCorporaXmlDocument.java
@@ -0,0 +1,163 @@
+package ru.textanalysis.tawt.ms.dictionary.open.corpora;
+
+import lombok.extern.slf4j.Slf4j;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import javax.management.modelmbean.XMLParseException;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.File;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Предоставляет создание нового xml файла,
+ * открытие существующего xml файла в стандарте OpenCorpora,
+ * добавление новых лемм в xml файлом
+ */
+@Slf4j
+public class OpenCorporaXmlDocument {
+
+ private Document doc;
+ private org.w3c.dom.Element lemmataElement;
+ private org.w3c.dom.Element linksElement;
+ private int lemmaId;
+
+ /**
+ * Instantiates a new Open corpora xml document.
+ */
+ public OpenCorporaXmlDocument() {
+ try {
+ DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
+ this.doc = dBuilder.newDocument();
+
+ org.w3c.dom.Element rootElement = doc.createElement("dictionary");
+ doc.appendChild(rootElement);
+ org.w3c.dom.Element grammemesElement = doc.createElement("grammemes");
+ rootElement.appendChild(grammemesElement);
+ org.w3c.dom.Element restrictionsElement = doc.createElement("restrictions");
+ rootElement.appendChild(restrictionsElement);
+ this.lemmataElement = doc.createElement("lemmata");
+ rootElement.appendChild(lemmataElement);
+ org.w3c.dom.Element linkTypesElement = doc.createElement("link_types");
+ rootElement.appendChild(linkTypesElement);
+ this.linksElement = doc.createElement("links");
+ rootElement.appendChild(linksElement);
+
+ lemmaId = 100000001;
+ } catch (ParserConfigurationException e) {
+ log.warn(e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Instantiates a new Open corpora xml document.
+ *
+ * @param filePath путь до файла xml в стандарте OpenCorpora
+ * @param lemmas List в котором хранятся уже добавленные леммы, для предотвращения дублирования
+ */
+ public OpenCorporaXmlDocument(String filePath, List lemmas) {
+ try {
+ DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
+ dbFactory.setIgnoringElementContentWhitespace(true);
+ dbFactory.setNamespaceAware(true);
+ DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
+ this.doc = dBuilder.parse(new File(filePath));
+ doc.getDocumentElement().normalize();
+
+ org.w3c.dom.Element rootElement = doc.getDocumentElement();
+ deleteEmptyNodes(rootElement);
+ if (Objects.equals(rootElement.getNodeName(), "dictionary")) {
+ NodeList rootElementChilds = rootElement.getChildNodes();
+ for (int i = 0; i < rootElementChilds.getLength(); i++) {
+ Node nNode = rootElementChilds.item(i);
+ if (nNode.getNodeType() == Node.ELEMENT_NODE) {
+ org.w3c.dom.Element eElement = (org.w3c.dom.Element) nNode;
+ if (eElement.getTagName().equals("lemmata")) {
+ this.lemmataElement = eElement;
+ NodeList lemmataElementChilds = lemmataElement.getChildNodes();
+ for (int j = 0; j < lemmataElementChilds.getLength(); j++) {
+ Node lNode = lemmataElementChilds.item(j);
+ if (lNode.getNodeType() == Node.ELEMENT_NODE) {
+ org.w3c.dom.Element lElement = (org.w3c.dom.Element) lNode;
+ if (lElement.getTagName().equals("lemma")) {
+ if (this.lemmaId < Integer.parseInt(lElement.getAttributeNode("id").getValue())) {
+ this.lemmaId = Integer.parseInt(lElement.getAttributeNode("id").getValue());
+ }
+ NodeList lemmaElementChilds = lElement.getChildNodes();
+ for (int k = 0; k < lemmaElementChilds.getLength(); k++) {
+ Node tNode = lemmaElementChilds.item(k);
+ if (tNode.getNodeType() == Node.ELEMENT_NODE) {
+ org.w3c.dom.Element tElement = (org.w3c.dom.Element) tNode;
+ if (tElement.getTagName().equals("l")) {
+ synchronized (lemmas) {
+ lemmas.add(tElement.getAttributeNode("t").getValue());
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ this.lemmaId++;
+ } else if (eElement.getTagName().equals("links")) {
+ this.linksElement = eElement;
+ }
+ }
+ }
+ } else {
+ throw new XMLParseException();
+ }
+ if (this.lemmataElement == null || this.linksElement == null) {
+ throw new XMLParseException();
+ }
+ if (lemmaId == 0) {
+ lemmaId = 100000001;
+ }
+ } catch (XMLParseException ex) {
+ log.error("Неправильный формат xml файла.", ex);
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ log.error(ex.getMessage(), ex);
+ }
+ }
+
+ public Document getDoc() {
+ return doc;
+ }
+
+ public Element getLemmataElement() {
+ return lemmataElement;
+ }
+
+ public Element getLinksElement() {
+ return linksElement;
+ }
+
+ public int getLemmaId() {
+ return lemmaId;
+ }
+
+ public void setLemmaId(int lemmaId) {
+ this.lemmaId = lemmaId;
+ }
+
+ private void deleteEmptyNodes(org.w3c.dom.Element element) {
+ if (element.hasChildNodes()) {
+ NodeList nodelist = element.getChildNodes();
+ for (int i = nodelist.getLength() - 1; i >= 0; i--) {
+ if (nodelist.item(i).getNodeType() != Node.ELEMENT_NODE) {
+ element.removeChild(nodelist.item(i));
+ }
+ }
+ for (int i = 0; i < element.getChildNodes().getLength(); i++) {
+ deleteEmptyNodes((org.w3c.dom.Element) element.getChildNodes().item(i));
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/textanalysis/tawt/ms/dictionary/open/corpora/XmlBuilder.java b/src/main/java/ru/textanalysis/tawt/ms/dictionary/open/corpora/XmlBuilder.java
new file mode 100644
index 0000000..0b24573
--- /dev/null
+++ b/src/main/java/ru/textanalysis/tawt/ms/dictionary/open/corpora/XmlBuilder.java
@@ -0,0 +1,103 @@
+package ru.textanalysis.tawt.ms.dictionary.open.corpora;
+
+import lombok.extern.slf4j.Slf4j;
+import ru.textanalysis.tawt.ms.dictionary.convertor.WiktionaryTagsData;
+import ru.textanalysis.tawt.ms.dictionary.convertor.WordFormForConverter;
+import ru.textanalysis.tawt.ms.dictionary.wiktionary.WiktionaryInfoParser;
+
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Основной класс
+ * Создание нового xml файла или открытие существующего
+ * Получение словоформ в стандарте OpenCorpora из Wiktionary
+ * Добавление новых лемм в xml файл
+ * Запись информации в xml файл
+ */
+@Slf4j
+public class XmlBuilder {
+
+ private final OpenCorporaXmlDocument xmlDocument;
+ private final XmlDocumentLemmaCreator lemmaCreator;
+ private final WiktionaryTagsData wiktionaryTagsData;
+ private final WiktionaryInfoParser wiktionaryInfoParser;
+ private final List lemmas;
+
+ /**
+ * Instantiates a new Xml builder.
+ */
+ public XmlBuilder() {
+ this.lemmas = Collections.synchronizedList(new ArrayList<>());
+ xmlDocument = new OpenCorporaXmlDocument();
+ wiktionaryTagsData = new WiktionaryTagsData();
+ lemmaCreator = new XmlDocumentLemmaCreator(wiktionaryTagsData, xmlDocument);
+ wiktionaryInfoParser = new WiktionaryInfoParser(wiktionaryTagsData);
+ }
+
+ /**
+ * Instantiates a new Xml builder.
+ *
+ * @param filePath путь до файла xml в формате OpenCorpora
+ */
+ public XmlBuilder(String filePath) {
+ this.lemmas = Collections.synchronizedList(new ArrayList<>());
+ xmlDocument = new OpenCorporaXmlDocument(filePath, this.lemmas);
+ wiktionaryTagsData = new WiktionaryTagsData();
+ lemmaCreator = new XmlDocumentLemmaCreator(wiktionaryTagsData, xmlDocument);
+ wiktionaryInfoParser = new WiktionaryInfoParser(wiktionaryTagsData);
+ }
+
+ /**
+ * Получение информации о слове и его тегах в стандарте OpenCorpora
+ *
+ * @param word исследуемое слово
+ * @param sleepTime время между запросами
+ * @param requestTimeOut время ожидания подключения
+ * @return the words tags
+ */
+ public List> getWordsTags(String word, int sleepTime, int requestTimeOut) {
+ return wiktionaryInfoParser.getWordsTags(word, sleepTime, requestTimeOut, lemmas);
+ }
+
+ /**
+ * Добавление новой формы в xml файл
+ *
+ * @param forms список форм в стандарте OpenCorpora
+ */
+ public void addLemmaForms(List> forms) {
+ synchronized (lemmaCreator) {
+ lemmaCreator.addLemmaForms(forms);
+ }
+ }
+
+ /**
+ * Записывает информацию в xml файл
+ *
+ * @param filePath путь до файла, в который будет идти запись
+ */
+ public synchronized void recordInFile(String filePath) {
+ try {
+ TransformerFactory transformerFactory = TransformerFactory.newInstance();
+ Transformer transformer = transformerFactory.newTransformer();
+ transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+ transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
+ transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
+
+ DOMSource domSource = new DOMSource(xmlDocument.getDoc());
+ StreamResult streamResult = new StreamResult(new File(filePath));
+
+ transformer.transform(domSource, streamResult);
+ } catch (TransformerException ex) {
+ log.error("Ошибка записи в файл.", ex);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/textanalysis/tawt/ms/dictionary/open/corpora/XmlDocumentLemmaCreator.java b/src/main/java/ru/textanalysis/tawt/ms/dictionary/open/corpora/XmlDocumentLemmaCreator.java
new file mode 100644
index 0000000..4bb6a0a
--- /dev/null
+++ b/src/main/java/ru/textanalysis/tawt/ms/dictionary/open/corpora/XmlDocumentLemmaCreator.java
@@ -0,0 +1,138 @@
+package ru.textanalysis.tawt.ms.dictionary.open.corpora;
+
+import lombok.extern.slf4j.Slf4j;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+import ru.textanalysis.tawt.ms.dictionary.convertor.WiktionaryTagsData;
+import ru.textanalysis.tawt.ms.dictionary.convertor.WordFormForConverter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static ru.textanalysis.tawt.ms.constant.Const.TAB_SEPARATOR;
+import static ru.textanalysis.tawt.ms.constant.TypeOfSpeechs.*;
+
+/**
+ * Добавление в xml файл новых лемм
+ */
+@Slf4j
+public class XmlDocumentLemmaCreator {
+
+ private final WiktionaryTagsData wiktionaryTagsData;
+ private final OpenCorporaXmlDocument xmlDocument;
+
+ /**
+ * Instantiates a new Xml document lemma creator.
+ *
+ * @param wiktionaryTagsData объект с информацией о преобразовании тегов
+ */
+ public XmlDocumentLemmaCreator(WiktionaryTagsData wiktionaryTagsData, OpenCorporaXmlDocument xmlDocument) {
+ this.wiktionaryTagsData = wiktionaryTagsData;
+ this.xmlDocument = xmlDocument;
+ }
+
+ /**
+ * Добавление новых лемм в xml файл
+ *
+ * @param forms добавляемые формы
+ */
+ public void addLemmaForms(List> forms) {
+ for (int i = 0; i < forms.size(); i++) {
+ if (forms.get(i).get(0).contains(VERB)) {
+ continue;
+ }
+ if (forms.get(i).get(0).contains(INFN) && i > 0 && forms.get(i - 1).get(0).contains(VERB)) {
+ synchronized (xmlDocument) {
+ addLemma(forms.get(i - 1));
+ addLemma(forms.get(i));
+ Element linkElement = createNewElement(xmlDocument.getLinksElement(), xmlDocument.getLemmaId() - 1, xmlDocument.getLemmaId() - 2, 3);
+ }
+ } else {
+ synchronized (xmlDocument) {
+ addLemma(forms.get(i));
+ }
+ }
+ if (forms.get(i).get(0).contains(ADJS) && i > 0 && forms.get(i - 1).get(0).contains(ADJF)) {
+ synchronized (xmlDocument) {
+ Element linkElement = createNewElement(xmlDocument.getLinksElement(), xmlDocument.getLemmaId() - 2, xmlDocument.getLemmaId() - 1, 1);
+ }
+ } else if (forms.get(i).get(0).contains(PRTS) && i > 0 && forms.get(i - 1).get(0).contains(PRTF)) {
+ synchronized (xmlDocument) {
+ Element linkElement = createNewElement(xmlDocument.getLinksElement(), xmlDocument.getLemmaId() - 2, xmlDocument.getLemmaId() - 1, 6);
+ }
+ }
+ }
+ }
+
+ private Element createNewElement(Element parentElement, String tagName, String attributeName, String attributeValue) {
+ Element newElement = xmlDocument.getDoc().createElement(tagName);
+ parentElement.appendChild(newElement);
+ Attr attr = xmlDocument.getDoc().createAttribute(attributeName);
+ attr.setValue(attributeValue);
+ newElement.setAttributeNode(attr);
+
+ return newElement;
+ }
+
+ private Element createNewElement(Element parentElement, int fromIndex, int toIndex, int linkType) {
+ Element newElement = xmlDocument.getDoc().createElement("link");
+ parentElement.appendChild(newElement);
+ Attr firstAttr = xmlDocument.getDoc().createAttribute("from");
+ firstAttr.setValue(String.valueOf(fromIndex));
+ newElement.setAttributeNode(firstAttr);
+ Attr secondAttr = xmlDocument.getDoc().createAttribute("to");
+ secondAttr.setValue(String.valueOf(toIndex));
+ newElement.setAttributeNode(secondAttr);
+ Attr thirdAttr = xmlDocument.getDoc().createAttribute("type");
+ thirdAttr.setValue(String.valueOf(linkType));
+ newElement.setAttributeNode(thirdAttr);
+
+ return newElement;
+ }
+
+ private void addLemma(List forms) {
+ try {
+ if (forms != null && forms.size() > 0) {
+ if (!wiktionaryTagsData.getToS().contains(forms.get(0).getTags().get(0))) {
+ throw new Exception("Неверный формат токена.");
+ }
+
+ Element lemmaElement = createNewElement(xmlDocument.getLemmataElement(), "lemma", "id", String.valueOf(xmlDocument.getLemmaId()));
+
+ Element lElement = createNewElement(lemmaElement, "l", "t", forms.get(0).getWord());
+
+ List overallTags;
+ if (forms.get(0).getTags().size() > 0) {
+ overallTags = new ArrayList<>(forms.get(0).getTags());
+ } else {
+ throw new Exception("Пустой токен.");
+ }
+
+ for (int i = 1; i < forms.size(); i++) {
+ List derivitiveTags = new ArrayList<>(forms.get(i).getTags());
+ overallTags.retainAll(derivitiveTags);
+ }
+
+ Element gElement;
+ for (String overallTag : overallTags) {
+ gElement = createNewElement(lElement, "g", "v", overallTag);
+ }
+
+ for (WordFormForConverter form : forms) {
+ Element fElement = createNewElement(lemmaElement, "f", "t", form.toString().split(TAB_SEPARATOR)[0]);
+ List derivitiveTags = new ArrayList<>(form.getTags());
+ derivitiveTags.removeAll(overallTags);
+ for (String derivitiveTag : derivitiveTags) {
+ gElement = createNewElement(fElement, "g", "v", derivitiveTag);
+ }
+ }
+
+ xmlDocument.setLemmaId(xmlDocument.getLemmaId() + 1);
+ }
+ } catch (NullPointerException ex) {
+ log.error("Неверный формат токена.", ex);
+ } catch (Exception ex) {
+ log.error(ex.getMessage(), ex);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/textanalysis/tawt/ms/dictionary/wiktionary/WiktionaryInfoParser.java b/src/main/java/ru/textanalysis/tawt/ms/dictionary/wiktionary/WiktionaryInfoParser.java
new file mode 100644
index 0000000..d54960a
--- /dev/null
+++ b/src/main/java/ru/textanalysis/tawt/ms/dictionary/wiktionary/WiktionaryInfoParser.java
@@ -0,0 +1,271 @@
+package ru.textanalysis.tawt.ms.dictionary.wiktionary;
+
+import lombok.extern.slf4j.Slf4j;
+import org.jsoup.nodes.Element;
+import org.jsoup.select.Elements;
+import ru.textanalysis.tawt.ms.dictionary.convertor.WiktionaryTagsData;
+import ru.textanalysis.tawt.ms.dictionary.convertor.WiktionaryToOpenCorporaConverter;
+import ru.textanalysis.tawt.ms.dictionary.convertor.WordFormForConverter;
+
+import java.net.SocketTimeoutException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Objects;
+
+import static java.lang.Thread.sleep;
+import static ru.textanalysis.tawt.ms.constant.Const.COMMA_SEPARATOR;
+import static ru.textanalysis.tawt.ms.constant.Const.TAB_SEPARATOR;
+
+/**
+ * Парсер информации о тегах слова из Wiktionary
+ */
+@Slf4j
+public class WiktionaryInfoParser {
+
+ private final WiktionaryToOpenCorporaConverter wiktionaryToOpenCorporaConverter;
+ private WiktionaryParsabilityChecker parsabilityChecker;
+
+ /**
+ * Instantiates a new Wiktionary info parser.
+ *
+ * @param wiktionaryTagsData объект с информацией о преобразовании тегов
+ */
+ public WiktionaryInfoParser(WiktionaryTagsData wiktionaryTagsData) {
+ this.wiktionaryToOpenCorporaConverter = new WiktionaryToOpenCorporaConverter(wiktionaryTagsData);
+ }
+
+ /**
+ * Получение информации о слове и его тегах с Wiktionary
+ *
+ * @param word исследуемое слово
+ * @param sleepTime время между запросами
+ * @param requestTimeOut время ожидания подключения
+ * @param lemmas List в котором хранятся уже добавленные леммы, для предотвращения дублирования
+ * @return the words tags
+ */
+ public List> getWordsTags(String word, int sleepTime, int requestTimeOut, List lemmas) {
+ List> lexems = new ArrayList<>();
+ if (lemmas.contains(word.toLowerCase(Locale.ROOT))) {
+ return new ArrayList<>();
+ }
+ if (word.isEmpty()) {
+ return new ArrayList<>();
+ }
+ if (!word.matches("[а-яА-ЯёЁ-]+")) {
+ return new ArrayList<>();
+ }
+ try {
+ sleep(sleepTime);
+ parsabilityChecker = new WiktionaryParsabilityChecker(word.toLowerCase(Locale.ROOT), requestTimeOut);
+ if (parsabilityChecker.getDoc() == null) {
+ sleep(sleepTime / 2);
+ boolean isSuccessful = parsabilityChecker.tryRepeatConnection(word.toLowerCase(Locale.ROOT), 2 * requestTimeOut);
+ if (!isSuccessful) {
+ throw new SocketTimeoutException("Неудачное повторное соединение.");
+ }
+ }
+ if (lemmas.contains(parsabilityChecker.getInitialForm().text().toLowerCase(Locale.ROOT))) {
+ return new ArrayList<>();
+ }
+ if (!parsabilityChecker.checkParsability(lemmas)) {
+ return new ArrayList<>();
+ }
+
+ Element table = null;
+ boolean isFoundRussianBlock = false;
+ boolean isFoundWordTags = false;
+ StringBuilder tagsStr = new StringBuilder();
+ Element mainElement = parsabilityChecker.getDoc().getElementsByClass("mw-parser-output").first();
+ Elements mainElements = mainElement != null ? mainElement.getAllElements() : null;
+ for (int i = 0; i < Objects.requireNonNull(mainElements).size(); i++) {
+ if (mainElements.get(i).tagName().equals("h1")) {
+ if (isFoundRussianBlock) {
+ break;
+ }
+ if (Objects.requireNonNull(mainElements.get(i).getElementsByClass("mw-headline").first()).text().equals("Русский")) {
+ isFoundRussianBlock = true;
+ continue;
+ }
+ }
+ if (isFoundRussianBlock) {
+ if (mainElements.get(i).tagName().equals("h3")) {
+ if (Objects.requireNonNull(mainElements.get(i).getElementsByClass("mw-headline").first()).text().equals("Морфологические и синтаксические свойства")) {
+ isFoundWordTags = true;
+ continue;
+ }
+ }
+ if (isFoundWordTags) {
+ if (mainElements.get(i).tagName().equals("table")) {
+ table = mainElements.get(i);
+ } else if (mainElements.get(i).tagName().equals("p")) {
+ tagsStr.append(mainElements.get(i).text().toLowerCase(Locale.ROOT));
+ tagsStr.append(COMMA_SEPARATOR);
+ } else if (mainElements.get(i).tagName().equals("h3") || mainElements.get(i).tagName().equals("h4")) {
+ List> info = parseInfo(table, tagsStr.toString(), word.toLowerCase(Locale.ROOT), lemmas);
+ isFoundWordTags = false;
+ table = null;
+ tagsStr = new StringBuilder();
+ lexems.addAll(info);
+ }
+ }
+ }
+ }
+
+ lexems.removeIf(lexem -> lemmas.contains(lexem.get(0).getWord()));
+
+ if (!tryAdd(parsabilityChecker.getInitialForm().text().toLowerCase(Locale.ROOT), lemmas)) {
+ return new ArrayList<>();
+ }
+
+ return lexems;
+ } catch (SocketTimeoutException exc) {
+ String messages;
+ if (Objects.equals(exc.getMessage(), "Неудачное повторное соединение.")) {
+ messages = "https://ru.wiktionary.org/wiki/" + word.toLowerCase(Locale.ROOT) + ". Неудачное повторное соединение.";
+ } else {
+ messages = "https://ru.wiktionary.org/wiki/" + word.toLowerCase(Locale.ROOT) + ". Не удалось установить соединение.";
+ }
+ log.error(messages, exc);
+ return null;
+ } catch (NullPointerException ex) {
+ log.error("Не удалось разобрать страницу.", ex);
+ return new ArrayList<>();
+ } catch (Exception ex) {
+ log.error(ex.getMessage(), ex);
+ return new ArrayList<>();
+ }
+ }
+
+ private List> parseInfo(Element table, String tagsStr, String word, List lemmas) {
+ List lemma = new ArrayList<>();
+ List additionalLemma = new ArrayList<>();
+ List> lexems = new ArrayList<>();
+ if (table == null) {
+ if (tagsStr.contains("наречие") || tagsStr.contains("союз") || tagsStr.contains("предлог")
+ || tagsStr.contains("частица") || tagsStr.contains("междометие") || tagsStr.contains("неизменяем")) {
+ lemma.add(wiktionaryToOpenCorporaConverter.convertImmutableToWordForm(word.toLowerCase(Locale.ROOT) + TAB_SEPARATOR + tagsStr));
+ }
+ } else {
+ Elements tags = table.getElementsByTag("tr");
+
+ if (tagsStr.contains("глагол")) {
+ additionalLemma.add(wiktionaryToOpenCorporaConverter.convertImmutableToWordForm(parsabilityChecker.getInitialForm().text().toLowerCase(Locale.ROOT) + TAB_SEPARATOR + tagsStr));
+ }
+ List tokenTags = new ArrayList<>();
+ for (int tag = 0; tag < tags.size(); tag++) {
+ Elements child = tags.get(tag).children();
+ int counter = 0;
+ int columnNumber = 0;
+ StringBuilder rowTokenTags = new StringBuilder();
+ boolean repeatingForm = false;
+ if (child.size() > tokenTags.size() || child.get(0).tagName().equals("th")) {
+ for (int j = 0; j < child.size(); j++) {
+ if (tag == 0 && j == 0 && child.get(j).tagName().equals("th")) {
+ continue;
+ }
+ int colSpan;
+ if (child.get(j).tagName().equals("th")) {
+ if (child.get(j).hasAttr("colspan")) {
+ colSpan = Integer.parseInt(child.get(j).attr("colspan"));
+ } else {
+ colSpan = 1;
+ }
+ if (tag == 0) {
+ for (int k = 0; k < colSpan; k++) {
+ tokenTags.add(child.get(j).text().toLowerCase(Locale.ROOT));
+ }
+ } else {
+ for (int k = 0; k < colSpan; k++) {
+ tokenTags.set(counter, tokenTags.get(counter) + COMMA_SEPARATOR + child.get(counter).text().toLowerCase(Locale.ROOT));
+ counter++;
+ }
+ }
+ } else if (child.get(j).tagName().equals("td")) {
+ if (!child.get(j).children().isEmpty() && !child.get(j).children().get(0).tagName().equals("br")) {
+ if (rowTokenTags.length() == 0) {
+ rowTokenTags = new StringBuilder(child.get(j).getElementsByTag("a").text().toLowerCase(Locale.ROOT));
+ } else {
+ rowTokenTags.append(COMMA_SEPARATOR);
+ rowTokenTags.append(child.get(j).getElementsByTag("a").text().toLowerCase(Locale.ROOT));
+ }
+ } else {
+ String[] tex = child.get(j).text().split(" ");
+ for (int p = 0; p < tex.length; p++) {
+ if (tex.length > 1 && changeSpecialCharacter(tex[0]).equals(changeSpecialCharacter(tex[1]))) {
+ repeatingForm = true;
+ }
+ if (!(p > 0 && repeatingForm)) {
+ tex[p] = changeSpecialCharacter(tex[p]);
+ if (!tex[p].equals("—")) {
+ String token = tex[p] + TAB_SEPARATOR + tagsStr + COMMA_SEPARATOR + tokenTags.get(columnNumber) + COMMA_SEPARATOR + rowTokenTags;
+ if (token.contains("он она оно") && tex.length > 1) {
+ if (p == 0) {
+ token += ",муж. р.";
+ }
+ if (p == 1) {
+ token += ",жен. р.";
+ }
+ if (p == 2) {
+ token += ",ср. р.";
+ }
+ }
+ if (token.contains(",м.")) {
+ continue;
+ }
+ if ((rowTokenTags.toString().contains("я") || rowTokenTags.toString().contains("ты") || rowTokenTags.toString().contains("вы")
+ || rowTokenTags.toString().contains("они")) && tokenTags.get(columnNumber).contains("прош.")) {
+ continue;
+ } else if ((rowTokenTags.toString().contains("он она оно") || rowTokenTags.toString().contains("мы"))
+ && tokenTags.get(columnNumber).contains("прош.")) {
+ token = token.replaceAll("он она оно", "").replaceAll(",мы", "");
+ }
+ if (token.contains("кратк. форма")) {
+ additionalLemma.add(wiktionaryToOpenCorporaConverter.convertTableFormToWordForm(token.toLowerCase(Locale.ROOT)));
+ } else {
+ lemma.add(wiktionaryToOpenCorporaConverter.convertTableFormToWordForm(token.toLowerCase(Locale.ROOT)));
+ }
+ }
+ }
+ }
+ columnNumber++;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (lemma.size() > 0) {
+ lexems.add(lemma);
+ if (additionalLemma.size() > 0) {
+ lexems.add(additionalLemma);
+ }
+ }
+
+ return lexems;
+ }
+
+ private boolean tryAdd(String initialForm, List lemmas) {
+ synchronized (lemmas) {
+ if (!lemmas.contains(initialForm)) {
+ lemmas.add(initialForm);
+ return true;
+ }
+ return false;
+ }
+ }
+
+ private String changeSpecialCharacter(String s) {
+ s = s.replaceAll("́", "").replaceAll("́ѐ", "е").replaceAll("́о̀", "о")
+ .replaceAll("а̀", "а").replaceAll("ѐ", "е")
+ .replaceAll("ѝ", "и").replaceAll("о̀", "о")
+ .replaceAll("у̀", "у").replaceAll("ё̀", "ё")
+ .replaceAll("э̀", "э").replaceAll(COMMA_SEPARATOR, "")
+ .replaceAll("ѝ", "и").replaceAll("я̀", "я")
+ .replaceAll("о̀", "о").replaceAll("о̀", "о")
+ .replaceAll("у̀", "у").replaceAll("я̀", "я")
+ .replaceAll("ы̀", "ы").replaceAll("ю̀", "ю");
+ return s;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/textanalysis/tawt/ms/dictionary/wiktionary/WiktionaryParsabilityChecker.java b/src/main/java/ru/textanalysis/tawt/ms/dictionary/wiktionary/WiktionaryParsabilityChecker.java
new file mode 100644
index 0000000..b6aa8e2
--- /dev/null
+++ b/src/main/java/ru/textanalysis/tawt/ms/dictionary/wiktionary/WiktionaryParsabilityChecker.java
@@ -0,0 +1,160 @@
+package ru.textanalysis.tawt.ms.dictionary.wiktionary;
+
+import lombok.extern.slf4j.Slf4j;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
+import org.jsoup.select.Elements;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Collectors;
+
+import static ru.textanalysis.tawt.ms.constant.Const.COMMA_SEPARATOR;
+
+/**
+ * Установка соединение с Wiktionary,
+ * получение html страницы с информацией о словах
+ */
+@Slf4j
+public class WiktionaryParsabilityChecker {
+
+ public final String WIKTIONARY_URL = "https://ru.wiktionary.org/wiki/";
+ public final String USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:25.0) Gecko/20100101 Firefox/25.0";
+ public final String REFERRER = "https://www.yandex.ru";
+ public final boolean IGNORE_HTTP_ERRORS = Boolean.TRUE;
+ public final boolean FOLLOW_REDIRECTS = Boolean.TRUE;
+ public final boolean IGNORE_CONTENT_TYPE = Boolean.TRUE;
+ public final String ELEMENTS_BY_TAG_H1 = "h1";
+
+ private Document doc;
+ private Element initialForm;
+
+ /**
+ * получение html страницы для необходимого слова
+ *
+ * @param word слово, для которого получается страница
+ * @param requestTimeOut ождание время подключения в мс
+ */
+ public WiktionaryParsabilityChecker(String word, int requestTimeOut) {
+ String url = WIKTIONARY_URL + word;
+ try {
+ this.doc = Jsoup.connect(url)
+ .ignoreHttpErrors(IGNORE_HTTP_ERRORS)
+ .timeout(requestTimeOut)
+ .userAgent(USER_AGENT)
+ .referrer(REFERRER)
+ .followRedirects(FOLLOW_REDIRECTS)
+ .ignoreContentType(IGNORE_CONTENT_TYPE)
+ .get();
+ this.initialForm = doc.getElementsByTag(ELEMENTS_BY_TAG_H1).first();
+ } catch (IOException ex) {
+ log.warn(url + " Не удалось установить соединение.", ex);
+ }
+ }
+
+ /**
+ * Повтор подключения к странице Wiktionary
+ * если страница уже была получена - сразу возвращает true
+ *
+ * @param word слово, для которого получается страница
+ * @param requestTimeOut ожидание время подключения в мс
+ * @return true если попытка удалась, иначе false
+ */
+ public boolean tryRepeatConnection(String word, int requestTimeOut) {
+ String url = WIKTIONARY_URL + word;
+ try {
+ if (this.doc == null) {
+ this.doc = Jsoup.connect(url)
+ .ignoreHttpErrors(IGNORE_HTTP_ERRORS)
+ .timeout(requestTimeOut)
+ .userAgent(USER_AGENT)
+ .referrer(REFERRER)
+ .followRedirects(FOLLOW_REDIRECTS)
+ .ignoreContentType(IGNORE_CONTENT_TYPE)
+ .get();
+ this.initialForm = doc.getElementsByTag(ELEMENTS_BY_TAG_H1).first();
+ }
+ return true;
+ } catch (IOException ex) {
+ log.warn(url + " Не удалось установить соединение.", ex);
+ return false;
+ }
+ }
+
+ /**
+ * Проверка возможности получения информации о слове
+ * существует ли страница с данным словом,
+ * было ли оно уже добавлено в словарь,
+ * есть ли блок русского языка
+ *
+ * @param lemmas List в котором хранятся уже добавленные леммы, для предотвращения дублирования
+ * @return true если попытка удалась, иначе false
+ */
+ public boolean checkParsability(List lemmas) {
+ if (initialForm != null && !initialForm.text().isEmpty()) {
+ Element notFound = doc.getElementsByClass("noarticletext mw-content-ltr").first();
+ if (notFound != null) {
+ return false;
+ }
+ if (initialForm.text().toLowerCase(Locale.ROOT).contains("инкубатор")) {
+ return false;
+ }
+ List matchingElements = checkExistingForms(initialForm.text().toLowerCase(Locale.ROOT), lemmas);
+ if (!matchingElements.isEmpty()) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ Element content = doc.select("div.mw-parser-output").first();
+ AtomicBoolean isParsable = new AtomicBoolean(Boolean.FALSE);
+ if (content != null) {
+ Elements headLines = content.getElementsByTag(ELEMENTS_BY_TAG_H1);
+ headLines.forEach(headLine -> {
+ Elements head = headLine.select("span.mw-headline");
+ head.forEach(line -> {
+ if (line.text().equals("Русский")) {
+ isParsable.set(true);
+ }
+ });
+ });
+ }
+ if (!isParsable.get()) {
+ return false;
+ }
+
+ StringBuilder tags = new StringBuilder();
+ Elements wordProperties = Objects.requireNonNull(doc.getElementsByClass("mw-parser-output").first()).getElementsByTag("p");
+ if (!wordProperties.isEmpty()) {
+ tags.append(wordProperties.get(0).text().toLowerCase(Locale.ROOT)).append(COMMA_SEPARATOR);
+ if (wordProperties.size() > 1) {
+ tags.append(wordProperties.get(1).text().toLowerCase(Locale.ROOT)).append(COMMA_SEPARATOR);
+ }
+ if (wordProperties.size() > 2) {
+ tags.append(wordProperties.get(2).text().toLowerCase(Locale.ROOT)).append(COMMA_SEPARATOR);
+ }
+ }
+
+ return !tags.toString().contains("викисловарь:к удалению");
+ }
+
+ public Document getDoc() {
+ return doc;
+ }
+
+ public Element getInitialForm() {
+ return initialForm;
+ }
+
+ private List checkExistingForms(String initialForm, List lemmas) {
+ synchronized (lemmas) {
+ return lemmas.stream()
+ .filter(str -> str.trim().equals(initialForm))
+ .collect(Collectors.toList());
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/textanalysis/tawt/ms/loader/DatabaseLemmas.java b/src/main/java/ru/textanalysis/tawt/ms/loader/DatabaseLemmas.java
index 6da5318..82027f8 100644
--- a/src/main/java/ru/textanalysis/tawt/ms/loader/DatabaseLemmas.java
+++ b/src/main/java/ru/textanalysis/tawt/ms/loader/DatabaseLemmas.java
@@ -47,7 +47,7 @@ public void recreate(List> lemmas) {
public void decompressDd() {
boolean needDecompress;
if (file.exists()) {
- //todo проверка, что версия старая
+ //todo проверка, что версия старая. Проверка еще не реализована
needDecompress = false;
} else {
needDecompress = true;
diff --git a/src/main/java/ru/textanalysis/tawt/ms/loader/DatabaseStrings.java b/src/main/java/ru/textanalysis/tawt/ms/loader/DatabaseStrings.java
index 253ee18..660030d 100644
--- a/src/main/java/ru/textanalysis/tawt/ms/loader/DatabaseStrings.java
+++ b/src/main/java/ru/textanalysis/tawt/ms/loader/DatabaseStrings.java
@@ -75,12 +75,8 @@ private String getLiteralById(int idKey, boolean isInitialForm) {
public void decompressDd(File file) {
boolean needDecompress;
- if (file.exists()) {
- //todo проверка, что версия старая
- needDecompress = false;
- } else {
- needDecompress = true;
- }
+ //todo проверка, что версия старая
+ needDecompress = !file.exists();
if (needDecompress) {
System.out.println("Decompress DB. Please wait a few minutes");
File dir = file.getParentFile();
@@ -110,7 +106,7 @@ public void recreate(List> forms) {
.filter(form -> !form.isFirstKey())
.forEach(form -> {
int id = form.getKey();
- String name = form.getStringName();
+ String name = form.getStringName().replace("'", "");
String query = String.format(CONTINUED_INSERT, id, name);
if (form.isInitialForm()) {
insert(multipleInsertInitial, query, dbInitialFormString);
diff --git a/src/main/java/ru/textanalysis/tawt/ms/model/Property.java b/src/main/java/ru/textanalysis/tawt/ms/model/Property.java
index 0b2f259..86cc567 100644
--- a/src/main/java/ru/textanalysis/tawt/ms/model/Property.java
+++ b/src/main/java/ru/textanalysis/tawt/ms/model/Property.java
@@ -53,6 +53,7 @@ public final class Property {
public final static String NAME_BD_DERIVATIVE_FORM = "dictionary.derivativeFormString.db";
public final static String NAME_BD_INITIAL_FORM = "dictionary.initialFormString.db";
public final static String NAME_HASH_AND_MORF_CHARACTERISTICS = "dictionary.format.morfCharacteristic";
+ public final static String OMO_FORM_STAT = "dictionary.format.omoFormStat";
public final static int CONTROL_VALUE = -1;
public final static int KEY_OFFSET = 8;
public final static int START_ID_INITIAL_FORM = 1 << KEY_OFFSET;
diff --git a/src/main/java/ru/textanalysis/tawt/ms/model/jmorfsdk/DerivativeForm.java b/src/main/java/ru/textanalysis/tawt/ms/model/jmorfsdk/DerivativeForm.java
index 6eb5e41..a92442e 100644
--- a/src/main/java/ru/textanalysis/tawt/ms/model/jmorfsdk/DerivativeForm.java
+++ b/src/main/java/ru/textanalysis/tawt/ms/model/jmorfsdk/DerivativeForm.java
@@ -7,8 +7,8 @@ public final class DerivativeForm extends Form {
private InitialForm initialForm;
- public DerivativeForm(int formKey, long morfCharacteristics) {
- super(formKey, morfCharacteristics);
+ public DerivativeForm(int formKey, long morfCharacteristics, long link) {
+ super(formKey, morfCharacteristics, link);
}
@Override
diff --git a/src/main/java/ru/textanalysis/tawt/ms/model/jmorfsdk/Form.java b/src/main/java/ru/textanalysis/tawt/ms/model/jmorfsdk/Form.java
index daecc7f..edaaa1c 100644
--- a/src/main/java/ru/textanalysis/tawt/ms/model/jmorfsdk/Form.java
+++ b/src/main/java/ru/textanalysis/tawt/ms/model/jmorfsdk/Form.java
@@ -4,6 +4,7 @@
import ru.textanalysis.tawt.ms.loader.DatabaseFactory;
import ru.textanalysis.tawt.ms.loader.DatabaseStrings;
+import static ru.textanalysis.tawt.ms.constant.Const.COMMA_SEPARATOR;
import static ru.textanalysis.tawt.ms.loader.LoadHelper.getControlHashCode;
import static ru.textanalysis.tawt.ms.loader.LoadHelper.getControlValue;
@@ -16,11 +17,13 @@ public abstract class Form {
protected static int formCount = 0;
protected final long morphCharacteristics;
+ protected final long link;
protected final int formKeyInBD;
protected final int order;
- protected Form(int formKey, long morphCharacteristics) {
+ protected Form(int formKey, long morphCharacteristics, long link) {
this.morphCharacteristics = morphCharacteristics;
+ this.link = link;
this.formKeyInBD = formKey;
formCount++;
order = formCount;
@@ -30,6 +33,10 @@ public int getMyFormKey() {
return formKeyInBD;
}
+ public long getLink() {
+ return link;
+ }
+
public String getMyString() {
return DATABASE_FORM_STRINGS.getLiteralById(getMyFormKey());
}
@@ -97,11 +104,11 @@ public int hashCode() {
@Override
public String toString() {
return "{" +
- "TF=" + getTypeForm() + "," +
- "isInit=" + isInitialForm() + "," +
- "hash=" + hashCode() + "," +
+ "TF=" + getTypeForm() + COMMA_SEPARATOR +
+ "isInit=" + isInitialForm() + COMMA_SEPARATOR +
+ "hash=" + hashCode() + COMMA_SEPARATOR +
"str='" + getMyString() + "'," +
- "ToS=" + getTypeOfSpeech() + "," +
+ "ToS=" + getTypeOfSpeech() + COMMA_SEPARATOR +
"morf=" + morphCharacteristics +
"}";
}
diff --git a/src/main/java/ru/textanalysis/tawt/ms/model/jmorfsdk/InitialForm.java b/src/main/java/ru/textanalysis/tawt/ms/model/jmorfsdk/InitialForm.java
index 5032cae..c50054f 100644
--- a/src/main/java/ru/textanalysis/tawt/ms/model/jmorfsdk/InitialForm.java
+++ b/src/main/java/ru/textanalysis/tawt/ms/model/jmorfsdk/InitialForm.java
@@ -14,8 +14,8 @@ public class InitialForm extends Form {
private final List derivativeForms;
@Builder
- protected InitialForm(int formKey, byte typeOfSpeech, long morfCharacteristics, List derivativeForms) {
- super(formKey, morfCharacteristics);
+ protected InitialForm(int formKey, byte typeOfSpeech, long morfCharacteristics, long link, List derivativeForms) {
+ super(formKey, morfCharacteristics, link);
this.typeOfSpeech = typeOfSpeech;
this.derivativeForms = Collections.unmodifiableList(derivativeForms);
derivativeForms.forEach(form -> form.setInitialForm(this));
diff --git a/src/main/java/ru/textanalysis/tawt/ms/model/jmorfsdk/NumberForm.java b/src/main/java/ru/textanalysis/tawt/ms/model/jmorfsdk/NumberForm.java
index 2dd1628..dc9a541 100644
--- a/src/main/java/ru/textanalysis/tawt/ms/model/jmorfsdk/NumberForm.java
+++ b/src/main/java/ru/textanalysis/tawt/ms/model/jmorfsdk/NumberForm.java
@@ -12,7 +12,7 @@ public class NumberForm extends InitialForm {
private final String textNumber;
public NumberForm(String textNumber) {
- super(textNumber.hashCode(), TypeOfSpeech.NUMERAL, 0, List.of());
+ super(textNumber.hashCode(), TypeOfSpeech.NUMERAL, 0, 0, List.of());
this.textNumber = textNumber;
}
diff --git a/src/main/java/ru/textanalysis/tawt/ms/model/jmorfsdk/UnfamiliarForm.java b/src/main/java/ru/textanalysis/tawt/ms/model/jmorfsdk/UnfamiliarForm.java
index 30d45e1..3caadf2 100644
--- a/src/main/java/ru/textanalysis/tawt/ms/model/jmorfsdk/UnfamiliarForm.java
+++ b/src/main/java/ru/textanalysis/tawt/ms/model/jmorfsdk/UnfamiliarForm.java
@@ -12,7 +12,7 @@ public final class UnfamiliarForm extends InitialForm {
private final String literal;
public UnfamiliarForm(String literal) {
- super(literal.hashCode(), MorfologyParameters.TypeOfSpeech.UNFAMILIAR, 0, List.of());
+ super(literal.hashCode(), MorfologyParameters.TypeOfSpeech.UNFAMILIAR, 0, 0, List.of());
this.literal = literal;
}