LugsImportService.java
/*
* Copyright © 2014-2025 The CTAN Team and individual authors
*
* This file is distributed under the 3-clause BSD license.
* See file LICENSE for details.
*/
package org.ctan.site.services.lugs;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import org.ctan.site.CtanConfiguration.LugsConfig;
import org.ctan.site.domain.site.Lug;
import org.ctan.site.stores.LugStore;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Element;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import lombok.NonNull;
/**
* This service deals with the LUG database – Local User's Groups.
*
* @author <a href="mailto:gene@ctan.org">Gerd Neugebauer</a>
*/
public class LugsImportService {
/**
* The field <code>url</code> contains the URL of the lugs database.
*/
private String url;
/**
* The field <code>store</code> contains the underlying store for LUGs.
*/
private LugStore store;
/**
* This is the constructor for the class <code>LugsImportService</code>.
*
* @param config the configuration
* @param store the store
*/
@SuppressFBWarnings(value = "CT_CONSTRUCTOR_THROW")
public LugsImportService(@NonNull LugsConfig config,
@NonNull LugStore store) {
this.url = config.getUrl();
this.store = store;
}
/**
* Download an Apache directory listing and extract the files contained.
*
* @param url the URL the query
*
* @return the list of files contained
*
* @throws IOException in case of an I/O error
* @throws MalformedURLException in case of an error
* @throws URISyntaxException in case of an error
*/
private List<String> fileList(String url)
throws MalformedURLException,
IOException,
URISyntaxException {
List<String> list = new ArrayList<>();
var doc = Jsoup.parse(new URI(url).toURL(), 0);
for (Element el : doc.getElementsByTag("a")) {
var href = el.attr("href");
if (href.length() <= 3) {
list.add(href);
}
}
return list;
}
private String normalizeAddress(String a) {
return a.replaceAll(";", "\\\\");
}
private String normalizeEmail(String e) {
return e.replaceAll(" at ", "@");
}
/**
* Contact the NTG site to download the LUG database and store the entries
* in the site database.
*
* @throws IOException in case of an I/O error
* @throws InvocationTargetException in case of an error
* @throws IllegalArgumentException in case of an error
* @throws IllegalAccessException in case of an error
* @throws MalformedURLException in case of an error
* @throws NoSuchMethodException in case of an error
* @throws SecurityException in case of an error
* @throws URISyntaxException in case of an error
*/
public void update()
throws MalformedURLException,
IOException,
NoSuchMethodException,
SecurityException,
IllegalAccessException,
IllegalArgumentException,
InvocationTargetException,
URISyntaxException {
var lugs = store.findAll();
for (String code : fileList(url)) {
updateLug(new URI(url + code).toURL());
lugs.removeIf(lug -> lug.getCode().equals(code));
}
store.drop(lugs);
}
/**
* Parse the URL given and make an associated Lug instance.
*
* @param url the URL to read from
* @return the new or updated Lug instance
*
* @throws IOException in case of an error
* @throws SecurityException in case of an error
* @throws NoSuchMethodException in case of an error
* @throws InvocationTargetException in case of an error
* @throws IllegalArgumentException in case of an error
* @throws IllegalAccessException in case of an error
*/
private Lug updateLug(URL url)
throws IOException,
NoSuchMethodException,
SecurityException,
IllegalAccessException,
IllegalArgumentException,
InvocationTargetException {
var lug = Lug.builder().build();
try (var in = new BufferedReader(
new InputStreamReader(url.openStream(), StandardCharsets.UTF_8))) {
String s;
while ((s = in.readLine()) != null) {
s = s.trim();
if (s.length() == 0 || s.startsWith("#")) {
continue;
}
var kv = s.split("\t");
if (kv.length < 2 || kv[0].length() == 0
|| kv[1].length() == 0) {
continue;
}
var val = kv[1];
switch (kv[0]) {
case "addr":
lug.setAddress(normalizeAddress(val));
continue;
case "bacc":
lug.getBank().setAccount(val);
continue;
case "baddr":
lug.getBank().setAddress(normalizeAddress(val));
continue;
case "bank":
lug.getBank().setName(val);
continue;
case "bcode":
lug.getBank().setSwift(val);
continue;
case "code":
lug = store.findOrCreateByCode(val);
lug.setCountry(lug.getCode().substring(0, 2));
continue;
case "brout":
lug.getBank().setBic(val);
continue;
case "editor":
lug.getPeriodical().setEditorName(val);
continue;
case "eemail":
lug.getPeriodical().setEditorEmail(normalizeEmail(val));
continue;
case "email":
lug.setEmail(normalizeEmail(val));
continue;
case "faddr":
lug.getFinancial().setAddress(normalizeAddress(val));
continue;
case "femail":
lug.getFinancial().setEmail(normalizeEmail(val));
continue;
case "ffax":
lug.getFinancial().setFax(val);
continue;
case "fname":
lug.getFinancial().setName(val);
continue;
case "fphone":
lug.getFinancial().setPhone(val);
continue;
case "fpos":
lug.getFinancial().setPosition(val);
continue;
case "full":
lug.setFullName(val);
continue;
case "gaddr":
lug.getGeneral().setAddress(normalizeAddress(val));
continue;
case "gemail":
lug.getGeneral().setEmail(normalizeEmail(val));
continue;
case "gfax":
lug.getGeneral().setFax(val);
continue;
case "gname":
lug.getGeneral().setName(val);
continue;
case "gphone":
lug.getGeneral().setPhone(val);
continue;
case "gpos":
lug.getGeneral().setPosition(val);
continue;
case "lang":
lug.setLanguages(val);
continue;
case "list":
lug.setMailinglist(val);
continue;
case "members":
lug.setNumberOfMembers(Integer.parseInt(val));
continue;
case "paddr":
lug.getPolicy().setAddress(normalizeAddress(val));
continue;
case "pemail":
lug.getPolicy().setEmail(normalizeEmail(val));
continue;
case "pfax":
lug.getPolicy().setFax(val);
continue;
case "pname":
lug.getPolicy().setName(val);
continue;
case "pphone":
lug.getPolicy().setPhone(val);
continue;
case "ppos":
lug.getPolicy().setPosition(val);
continue;
case "publ":
lug.getPeriodical().setTitle(val);
continue;
case "short":
lug.setShortName(val);
continue;
case "subscr":
lug.setMailinglistSubscribe(val);
continue;
case "www":
lug.setWebsite(val);
continue;
default:
throw new IOException(
"Unknown lug attribute: " + kv[0]);
}
}
}
return lug;
}
}