summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/Group.java')
-rw-r--r--src/Group.java560
1 files changed, 560 insertions, 0 deletions
diff --git a/src/Group.java b/src/Group.java
new file mode 100644
index 0000000..3e1a1f2
--- /dev/null
+++ b/src/Group.java
@@ -0,0 +1,560 @@
+import java.util.HashMap;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+
+import java.io.BufferedReader;
+import java.io.PrintWriter;
+import java.io.FileWriter;
+import java.io.FileInputStream;
+import java.io.InputStreamReader;
+import java.io.IOException;
+
+import java.net.URL;
+
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamReader;
+
+public class Group extends Thread
+{
+ private static final HashMap<String, Group> KNOWN_GROUPS;
+ private final HashMap<String, PrintWriter> class_files;
+ private final String name;
+ private final String abbreviation;
+ private final String[] url;
+ private final Group.Type[] type;
+
+ static
+ {
+ KNOWN_GROUPS = new HashMap<String, Group>();
+ }
+
+ private Group
+ (
+ final String name,
+ final String abbreviation,
+ final String[] types_and_urls
+ )
+ {
+ final int sources;
+ int j;
+
+ sources = (types_and_urls.length / 2);
+
+ this.name = name;
+ this.abbreviation = abbreviation;
+
+ url = new String[sources];
+ type = new Group.Type[sources];
+
+ for (int i = 0; i < sources; ++i)
+ {
+ j = (i * 2);
+ type[i] = Type.get(types_and_urls[j]);
+ url[i] = types_and_urls[j + 1];
+ }
+
+ class_files = new HashMap<String, PrintWriter>();
+ }
+
+ private void register ()
+ throws Group.AlreadyRegisteredException
+ {
+ if (KNOWN_GROUPS.containsKey(abbreviation))
+ {
+ throw (new Group.AlreadyRegisteredException());
+ }
+
+ KNOWN_GROUPS.put(abbreviation, this);
+ }
+
+ public String get_abbreviation ()
+ {
+ return abbreviation;
+ }
+
+ public void add_event (final Event event)
+ {
+ final PrintWriter pw;
+
+ pw =
+ class_files.get
+ (
+ Classes.abbreviation_from_name
+ (
+ event.get_name(),
+ false
+ )
+ );
+
+ pw.println(event.toString());
+ pw.flush();
+ }
+
+ public void add_ICS_fragment
+ (
+ final String fragment,
+ final String class_name,
+ final boolean lazy
+ )
+ {
+ final PrintWriter pw;
+
+ pw =
+ class_files.get
+ (
+ Classes.abbreviation_from_name(class_name, lazy)
+ );
+
+ // Escapes all ',' or ';' not directly preceded by '\'.
+ // That's quite an inefficient way to do it btw.
+ pw.print
+ (
+ fragment.replaceAll
+ (
+ "(?<!\\\\),",
+ "\\\\,"
+ ).replaceAll
+ (
+ "(?<!\\\\);",
+ "\\\\;"
+ )
+ );
+
+ pw.flush();
+ }
+
+ private static enum Type
+ {
+ CELCAT(true),
+ DHX_CAL(true),
+ ICS(false),
+ LAZY_ICS(false),
+ ICS_NOUID(false),
+ LAZY_ICS_NOUID(false),
+ CAMSI(false);
+
+ public final boolean uses_xml;
+
+ private Type (final boolean uses_xml)
+ {
+ this.uses_xml = uses_xml;
+ }
+
+ public static Type get (final String str)
+ {
+ if (str.equals("celcat"))
+ {
+ return CELCAT;
+ }
+ else if (str.equals("dhx_cal"))
+ {
+ return DHX_CAL;
+ }
+ else if (str.equals("ics_nouid"))
+ {
+ return ICS_NOUID;
+ }
+ else if (str.equals("lazy_ics_nouid"))
+ {
+ return LAZY_ICS_NOUID;
+ }
+ else if (str.equals("ics"))
+ {
+ return ICS;
+ }
+ else if (str.equals("lazy_ics"))
+ {
+ return LAZY_ICS;
+ }
+ else if (str.equals("camsi"))
+ {
+ return CAMSI;
+ }
+
+ return null;
+ }
+ }
+
+ private boolean create_class_files ()
+ {
+ try
+ {
+ for (final String abbr: Classes.get_all_abbreviations())
+ {
+ class_files.put
+ (
+ abbr,
+ new PrintWriter
+ (
+ new FileWriter
+ (
+ Parameters.get_output_directory()
+ + "/"
+ + abbreviation
+ + "_"
+ + abbr
+ + ".ics"
+ )
+ )
+ );
+ }
+
+ class_files.put
+ (
+ "unknown",
+ new PrintWriter
+ (
+ new FileWriter
+ (
+ Parameters.get_output_directory()
+ + "/"
+ + abbreviation
+ + "_unknown.ics"
+ )
+ )
+ );
+ }
+ catch (final IOException e)
+ {
+ Error.ERROR.from_thread
+ (
+ abbreviation,
+ ("Could not create output file: " + e.toString())
+ );
+
+ return false;
+ }
+
+ return true;
+ }
+
+ private void finalize_class_files ()
+ {
+ for (final PrintWriter pw: class_files.values())
+ {
+ pw.flush();
+ pw.close();
+ }
+ }
+
+ private InputStreamReader remove_fuckups
+ (
+ final InputStreamReader irs
+ )
+ throws Exception
+ {
+ final BufferedReader in;
+ final PrintWriter pw;
+ final String filename;
+ String input;
+ boolean first_line;
+
+ filename =
+ (
+ Parameters.get_output_directory()
+ + "/"
+ + abbreviation
+ + ".fuckup"
+ );
+
+ in = new BufferedReader(irs);
+ pw = new PrintWriter(filename);
+
+ first_line = true;
+
+ while ((input = in.readLine()) != null)
+ {
+ if (!input.equals(""))
+ {
+ if (first_line)
+ {
+ pw.println(input.trim().replaceFirst("^([\\W]+)<","<"));
+ first_line = false;
+ }
+ else
+ {
+ pw.println(input);
+ }
+ }
+ }
+
+ pw.close();
+ irs.close();
+
+ return (new InputStreamReader(new FileInputStream(filename)));
+ }
+
+ private void parse_source
+ (
+ final int id,
+ final Group.Type type,
+ final String url
+ )
+ throws Exception
+ {
+ final XMLInputFactory input_factory;
+ final XMLStreamReader stream_reader;
+ final StringBuilder sb;
+
+ if (type.uses_xml)
+ {
+ input_factory = XMLInputFactory.newInstance();
+ stream_reader =
+ input_factory.createXMLStreamReader
+ (
+ remove_fuckups(new InputStreamReader((new URL(url)).openStream(), "UTF-8"))
+ );
+ sb = new StringBuilder();
+
+ sb.append(abbreviation);
+ sb.append("_s");
+ sb.append(id);
+ sb.append("_event");
+ }
+ else
+ {
+ stream_reader = null;
+ sb = null;
+ }
+
+ switch (type)
+ {
+ case CELCAT:
+ CelcatParser.parse(stream_reader, this, sb.toString());
+ break;
+
+ case DHX_CAL:
+ DHXCalParser.parse(stream_reader, this, sb.toString());
+ break;
+
+ case ICS_NOUID:
+ ICSParser.parse(url, this, false, true);
+ break;
+
+ case LAZY_ICS_NOUID:
+ ICSParser.parse(url, this, true, true);
+ break;
+
+ case ICS:
+ ICSParser.parse(url, this, false, false);
+ break;
+
+ case LAZY_ICS:
+ ICSParser.parse(url, this, true, false);
+ break;
+
+ case CAMSI:
+ CAMSIParser.parse
+ (
+ new BufferedReader
+ (
+ new InputStreamReader
+ (
+ (new URL(url)).openStream()
+ )
+ ),
+ this,
+ (abbreviation + "_s" + id + "_event")
+ );
+ break;
+ }
+ }
+
+ @Override
+ public void run ()
+ {
+
+ if (!create_class_files())
+ {
+ return;
+ }
+
+ for (int i = 0; i < type.length; ++i)
+ {
+ try
+ {
+ parse_source(i, type[i], url[i]);
+ }
+ catch (final Exception e)
+ {
+ final StringBuilder sb;
+
+ sb = new StringBuilder();
+
+ sb.append("An error occured while parsing source '");
+ sb.append(url[i]);
+ sb.append("'");
+
+ Error.ERROR.from_thread
+ (
+ abbreviation,
+ sb.toString(),
+ e.getMessage()
+ );
+ }
+ }
+
+ finalize_class_files();
+ }
+
+ public static void read_all ()
+ {
+ BufferedReader br;
+ String input_line;
+ String[] data;
+ Group new_group;
+
+ br = null;
+
+ try
+ {
+ br =
+ new BufferedReader
+ (
+ new InputStreamReader
+ (
+ new FileInputStream(Parameters.get_groups_filename())
+ )
+ );
+
+ while ((input_line = br.readLine()) != null)
+ {
+ data = input_line.split("::");
+
+ if (data.length >= 4 && ((data.length % 2) == 0))
+ {
+ new_group =
+ new Group
+ (
+ data[0],
+ data[1],
+ Arrays.copyOfRange(data, 2, data.length)
+ );
+
+ try
+ {
+ new_group.register();
+ }
+ catch (final Group.AlreadyRegisteredException e)
+ {
+ final StringBuilder sb;
+
+ sb = new StringBuilder();
+
+ sb.append("Duplicate group entry for group \"");
+ sb.append(new_group.name);
+ sb.append("\".");
+
+ Error.ERROR.from_file
+ (
+ Parameters.get_groups_filename(),
+ sb.toString()
+ );
+
+ continue;
+ }
+ }
+ else
+ {
+ final StringBuilder sb;
+
+ sb = new StringBuilder();
+
+ sb.append("Invalid group entry: \"");
+ sb.append(input_line);
+ sb.append("\".");
+
+ Error.ERROR.from_file
+ (
+ Parameters.get_groups_filename(),
+ sb.toString()
+ );
+
+ continue;
+ }
+ }
+ }
+ catch (final Exception e)
+ {
+ Error.FATAL.from_file
+ (
+ Parameters.get_groups_filename(),
+ "Error while reading file:",
+ e.getMessage()
+ );
+
+ if (br != null)
+ {
+ try
+ {
+ br.close();
+ }
+ catch (final Exception e2)
+ {
+ Error.WARNING.from_file
+ (
+ Parameters.get_groups_filename(),
+ "Error while closing file:",
+ e2.getMessage()
+ );
+ }
+ }
+
+ return;
+ }
+
+ try
+ {
+ br.close();
+ }
+ catch (final Exception e)
+ {
+ Error.WARNING.from_file
+ (
+ Parameters.get_groups_filename(),
+ "Error while closing file:",
+ e.getMessage()
+ );
+ }
+ }
+
+ /**
+ * Creates Group threads (one per group) to translate each XML file.
+ * For performance reasons, we limit the number of threads running
+ * concurrently at a give time (also, I'm on shared hosting and the task
+ * doesn't actually need to be very fast so I avoid being too greedy).
+ **/
+ public static void run_all ()
+ {
+ final ExecutorService exec;
+ final List<Callable<Object>> groups;
+
+ groups = new ArrayList<Callable<Object>>();
+
+ exec = Executors.newFixedThreadPool(Parameters.get_max_threads());
+
+ for (final Group g: KNOWN_GROUPS.values())
+ {
+ groups.add(Executors.callable(g));
+ }
+
+ /** This won't return until all is done. **/
+ try
+ {
+ exec.invokeAll(groups);
+ }
+ catch (final InterruptedException ie)
+ {
+ Error.FATAL.from_thread("main", "Interrupted before the end.");
+ }
+
+ exec.shutdown();
+ }
+
+ private static class AlreadyRegisteredException extends Exception {}
+}