2 * Free Telco Dictionary
3 * Copyright © 2013 František Kučera (frantovo.cz)
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 package cz.frantovo.telco.dictionary;
20 import static cz.frantovo.telco.dictionary.Xmlns.*;
21 import static cz.frantovo.telco.dictionary.Functions.*;
22 import java.io.BufferedWriter;
23 import java.io.ByteArrayOutputStream;
24 import java.io.DataOutputStream;
26 import java.io.FileOutputStream;
27 import java.io.FileWriter;
28 import java.io.IOException;
29 import java.text.SimpleDateFormat;
30 import java.util.ArrayList;
31 import java.util.Date;
32 import java.util.List;
33 import java.util.SortedSet;
34 import java.util.TreeSet;
35 import java.util.logging.Level;
36 import java.util.logging.Logger;
37 import javax.xml.parsers.DocumentBuilder;
38 import javax.xml.parsers.DocumentBuilderFactory;
39 import javax.xml.parsers.ParserConfigurationException;
40 import javax.xml.transform.Transformer;
41 import javax.xml.transform.TransformerConfigurationException;
42 import javax.xml.transform.TransformerException;
43 import javax.xml.transform.TransformerFactory;
44 import javax.xml.transform.dom.DOMSource;
45 import javax.xml.transform.stream.StreamResult;
46 import javax.xml.transform.stream.StreamSource;
47 import javax.xml.xpath.XPath;
48 import javax.xml.xpath.XPathConstants;
49 import javax.xml.xpath.XPathExpression;
50 import javax.xml.xpath.XPathExpressionException;
51 import javax.xml.xpath.XPathFactory;
52 import org.w3c.dom.Document;
53 import org.w3c.dom.Node;
54 import org.w3c.dom.NodeList;
55 import org.xml.sax.SAXException;
59 * Generates dictionary files in StarDict format from source in our XML format.
63 * Number format should be: 32-bits unsigned number in network byte order
66 * @author Ing. František Kučera (frantovo.cz)
68 public class Generator {
70 private static final Logger log = Logger.getLogger(Generator.class.getName());
71 private static final String EML_TO_KEN = "ixumhht68";
72 private final DocumentBuilderFactory documentBuilderFactory;
73 private final DocumentBuilder documentBuilder;
74 private final XPathFactory xpathFactory;
75 private final XPath xpath;
76 private final TransformerFactory xslFactory;
77 private final Transformer xsl;
78 private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy.MM.dd");
80 public Generator() throws ParserConfigurationException, TransformerConfigurationException {
81 documentBuilderFactory = DocumentBuilderFactory.newInstance();
82 documentBuilderFactory.setNamespaceAware(true);
83 documentBuilder = documentBuilderFactory.newDocumentBuilder();
85 xslFactory = TransformerFactory.newInstance();
86 xsl = xslFactory.newTransformer(new StreamSource("concept.xsl"));
88 xpathFactory = XPathFactory.newInstance();
89 xpath = xpathFactory.newXPath();
90 xpath.setNamespaceContext(getNamespaceContext());
93 private void generate(File folder, String filePrefix) {
94 File infoFile = new File(folder, filePrefix + ".ifo");
95 File dictFile = new File(folder, filePrefix + ".dict");
96 File indexFile = new File(folder, filePrefix + ".idx");
97 File synonymFile = new File(folder, filePrefix + ".syn");
99 FileOutputStream dictOutputStream = null;
100 DataOutputStream synonymOutputStream = null;
101 DataOutputStream indexOutputStream = null;
102 BufferedWriter infoWriter = null;
104 SortedSet<IndexEntry> indexEntries = new TreeSet<>();
105 SortedSet<SynonymsEntry> synonymsEntries = new TreeSet<>();
108 dictOutputStream = new FileOutputStream(dictFile);
109 synonymOutputStream = new DataOutputStream(new FileOutputStream(synonymFile));
110 indexOutputStream = new DataOutputStream(new FileOutputStream(indexFile));
111 infoWriter = new BufferedWriter(new FileWriter(infoFile));
113 Document sourceDocument = documentBuilder.parse("../../data/dictionary.xml");
114 XPathExpression termsXPath = xpath.compile("d:term/@completeForm|d:term/@abbreviation");
115 // TODO: tags - labels/descriptions
116 xsl.setParameter("tags", sourceDocument.getElementsByTagNameNS(DICTIONARY, "tags").item(0));
119 long conceptIndex = 0;
120 for (Node conceptNode : nodeIterable(sourceDocument.getElementsByTagNameNS(DICTIONARY, "concept"))) {
121 ByteArrayOutputStream conceptXhtml = new ByteArrayOutputStream();
122 xsl.transform(new DOMSource(conceptNode), new StreamResult(conceptXhtml));
123 int length = conceptXhtml.size();
124 dictOutputStream.write(conceptXhtml.toByteArray());
126 NodeList nameNodes = (NodeList) termsXPath.evaluate(conceptNode, XPathConstants.NODESET);
127 List<String> names = new ArrayList<>();
129 for (Node nameNode : nodeIterable(nameNodes)) {
130 String name = nameNode.getTextContent().trim();
131 if (!name.isEmpty()) {
136 String baseName = names.get(0);
137 IndexEntry indexEntry = new IndexEntry(baseName, offset, length);
138 indexEntries.add(indexEntry);
140 for (int i = 1; i < names.size(); i++) {
141 String name = names.get(i);
142 if (!baseName.equals(name)) {
143 synonymsEntries.add(new SynonymsEntry(indexEntry, name));
147 offset = offset + length;
151 writeIndex(indexOutputStream, indexEntries);
152 writeSynonyms(synonymOutputStream, synonymsEntries);
154 indexOutputStream.flush();
155 writeInfo(infoWriter, sourceDocument, conceptIndex + 1, synonymsEntries.size(), indexFile.length());
156 } catch (SAXException | IOException | TransformerException | XPathExpressionException e) {
157 log.log(Level.SEVERE, "unable to generate", e);
159 close(dictOutputStream);
160 close(synonymOutputStream);
161 close(indexOutputStream);
166 private void writeIndex(DataOutputStream indexOutputStream, SortedSet<IndexEntry> indexEntries) throws IOException {
168 for (IndexEntry e : indexEntries) {
169 e.serialize(indexOutputStream);
170 e.setOrdinal(ordinal++);
174 private void writeSynonyms(DataOutputStream synonymOutputStream, SortedSet<SynonymsEntry> synonymsEntries) throws IOException {
175 for (SynonymsEntry s : synonymsEntries) {
176 s.serialize(synonymOutputStream);
180 private void writeInfo(BufferedWriter infoWriter, Document sourceDocument, long wordcount, long synwourdcount, long idxfilesize) throws IOException {
181 // TODO: values from document metadata
182 infoWriter.write("StarDict's dict ifo file\n");
183 infoWriter.write("version=2.4.2\n");
184 infoWriter.write("bookname=Free Telco Dictionary\n");
185 infoWriter.write("wordcount=" + wordcount + "\n");
186 infoWriter.write("synwordcount=" + synwourdcount + "\n");
187 infoWriter.write("idxfilesize=" + idxfilesize + "\n");
188 infoWriter.write("idxoffsetbits=32\n");
189 infoWriter.write("author=František Kučera\n");
190 infoWriter.write("em" + "ail=telco" + "-dictionary." + EML_TO_KEN + "@" + "fran" + "tovo.cz\n");
191 infoWriter.write("website=https://telco.frantovo.cz\n");
192 infoWriter.write("description=A dictionary for telecommunications licensed under GNU FDL\n");
193 infoWriter.write("date=" + dateFormat.format(new Date()) + "\n");
194 infoWriter.write("sametypesequence=h\n");
197 public static void main(String[] args) {
198 File outputFolder = new File("../../delivery/free-telco-dictionary");
199 outputFolder.mkdir();
202 Generator g = new Generator();
203 g.generate(outputFolder, "telco");
204 } catch (ParserConfigurationException | TransformerConfigurationException e) {
205 log.log(Level.SEVERE, "error during initialization", e);