aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNathanael Sensfelder <SpamShield0@MultiAgentSystems.org>2016-05-27 15:50:00 +0200
committerNathanael Sensfelder <SpamShield0@MultiAgentSystems.org>2016-05-27 15:50:00 +0200
commit94b924963d7927fba0bafa268c8477de2794a6da (patch)
tree3d1dc8533904a5cc0276d2a4f69d53a2d76d9bc2
downloadcalendar-94b924963d7927fba0bafa268c8477de2794a6da.zip
calendar-94b924963d7927fba0bafa268c8477de2794a6da.tar.bz2
Adds relevant data from antares
-rw-r--r--LICENSE24
-rw-r--r--Makefile10
-rw-r--r--data/CURRICULUMS10
-rw-r--r--data/L3INFO_RENNES_CLASSES17
-rw-r--r--data/L3INFO_RENNES_GROUPS1
-rw-r--r--data/M1INFO_CLASSES87
-rw-r--r--data/M1INFO_GROUPS10
-rw-r--r--data/M2CAMSI_CLASSES35
-rw-r--r--data/M2CAMSI_GROUPS1
-rw-r--r--data/M2DL_CLASSES131
-rw-r--r--data/M2DL_GROUPS1
-rw-r--r--data/M2IHM_CLASSES1
-rw-r--r--data/M2IHM_GROUPS1
-rw-r--r--data/M2IM_CLASSES23
-rw-r--r--data/M2IM_GROUPS1
-rw-r--r--data/M2RIT_IAICI_CLASSES30
-rw-r--r--data/M2RIT_IAICI_GROUPS1
-rw-r--r--data/M2RIT_RIBDM_CLASSES21
-rw-r--r--data/M2RIT_RIBDM_GROUPS1
-rw-r--r--data/M2RIT_RO_CLASSES78
-rw-r--r--data/M2RIT_RO_GROUPS1
-rw-r--r--data/M2RIT_RT_CLASSES36
-rw-r--r--data/M2RIT_RT_GROUPS1
-rw-r--r--data/M2RIT_SRLC_CLASSES28
-rw-r--r--data/M2RIT_SRLC_GROUPS1
-rw-r--r--src/CAMSIParser.java125
-rw-r--r--src/Calendar.java39
-rw-r--r--src/CelcatParser.java482
-rw-r--r--src/Classes.java341
-rw-r--r--src/DHXCalParser.java263
-rw-r--r--src/Error.java196
-rw-r--r--src/Event.java286
-rw-r--r--src/Group.java560
-rw-r--r--src/ICSParser.java99
-rw-r--r--src/Parameters.java135
35 files changed, 3077 insertions, 0 deletions
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..5692bf1
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,24 @@
+Copyright (c) 2015, Nathanael Sensfelder
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the <organization> nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL NATHANAEL SENSFELDER BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..e8db877
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,10 @@
+calendar:
+ javac src/*.java -d bin/
+
+clean:
+ rm -f output/*
+ rm -f bin/*.class
+
+test:
+ cd bin; \
+ java Calendar ../data/KNOWN_CLASSES ../data/GROUPS ../input ../output 8
diff --git a/data/CURRICULUMS b/data/CURRICULUMS
new file mode 100644
index 0000000..a575642
--- /dev/null
+++ b/data/CURRICULUMS
@@ -0,0 +1,10 @@
+M1 Informatique::M1INFO_GROUPS::M1INFO_CLASSES
+M2R IT - IAICI::M2RIT_IAICI_GROUPS::M2RIT_IAICI_CLASSES
+M2R IT - RIBDM::M2RIT_RIBDM_GROUPS::M2RIT_RIBDM_CLASSES
+M2R IT - RO::M2RIT_RO_GROUPS::M2RIT_RO_CLASSES
+M2R IT - RT::M2RIT_RT_GROUPS::M2RIT_RT_CLASSES
+M2R IT - SRLC::M2RIT_SRLC_GROUPS::M2RIT_SRLC_CLASSES
+M2 Image & Multimedia::M2IM_GROUPS::M2IM_CLASSES
+M2 Développement Logiciel::M2DL_GROUPS::M2DL_CLASSES
+M2 CAMSI::M2CAMSI_GROUPS::M2CAMSI_CLASSES
+L3 Informatique (Rennes)::L3INFO_RENNES_GROUPS::L3INFO_RENNES_CLASSES
diff --git a/data/L3INFO_RENNES_CLASSES b/data/L3INFO_RENNES_CLASSES
new file mode 100644
index 0000000..12f74a6
--- /dev/null
+++ b/data/L3INFO_RENNES_CLASSES
@@ -0,0 +1,17 @@
+Anglais::ANG::Anglais
+Algorithmique des graphes::AGR::AGR
+Architecture des ordinateurs 1::ARC1::ARC1
+Organisation des systèmes d'exploitation 1::SYR1::SYR1
+Langages formels::LF::LF
+Bases de la modélisation par objets::BMO::BMO
+Programmation 1::PRG1::PRG1
+::contrôle TP PRG1::PRG1
+Méthodes algorithmiques::ALG::ALGO
+::Contrôle Continu ALG::ALGO
+Architecture des ordinateurs 2::ARC2::ARC2
+Organisation et utilisation des systèmes d'exploitation 2::SYR2::SYR2
+Programmation 2::PRG2::PRG2
+Compilation::CMPL::COMP
+::Contrôle Projet CMPL::COMP
+Logique::LOG::LOG
+::MN.LOG::LOG
diff --git a/data/L3INFO_RENNES_GROUPS b/data/L3INFO_RENNES_GROUPS
new file mode 100644
index 0000000..699452b
--- /dev/null
+++ b/data/L3INFO_RENNES_GROUPS
@@ -0,0 +1 @@
+Groupe 1::L3INFO_RENNES_1::lazy_ics_nouid::https://planning.univ-rennes1.fr/jsp/custom/modules/plannings/anonymous_cal.jsp?data=8241fc387320021460eb67564d761472bd72d825015315fe9b2ebb35bd288075ec7f554d6ed7ba1bbad7b9bdf5b7bdb27fbb53e06bf966cea3b3f4c7cc4cd75b8af069ff1fd12df94a3649013d5b5ccc8edd3cda7b61e593
diff --git a/data/M1INFO_CLASSES b/data/M1INFO_CLASSES
new file mode 100644
index 0000000..7f45ace
--- /dev/null
+++ b/data/M1INFO_CLASSES
@@ -0,0 +1,87 @@
+Modèles et Concepts du Parallélisme et de la Répartition::MODELES CONCEPT PARA. (EM7INFAM)::MCPR
+::EM7INFAM (MODELES CONCEPT PARA.)::MCPR
+Modélisation, Conception et Programmation Orientée Objet::MODEL. CONCEPT PROG. (EM7INFBM)::MCPOO
+::EM7INFBM (MODEL. CONCEPT PROG.)::MCPOO
+Traduction des Langages::TRADUCT. LANGAGES (EM7INFCM)::TL
+::EM7INFCM (TRADUCT. LANGAGES)::TL
+Architecture et Programmation Parallèle::ARCH. PROG. PARALLELE (EM7INFDM)::APP
+::EM7INFDM (ARCH. PROG. PARALLELE)::APP
+Systèmes de Bases de Données Relationnels::SYS BD RELATIONNELS (EM7INFEM)::SBDR
+::EM7INFEM (SYS BD RELATIONNELS)::SBDR
+Techniques de base pour la Résolution des Problèmes::TECH. BASE RESOL. PROB. (EM7INFFM)::TRP
+::EM7INFFM (TECH. BASE RESOL. PROB.)::TRP
+Outils Informatiques pour le Multimédia::OUTILS INFO MULTIMEDIA (EM7INFGM)::OIM
+::EM7INFGM (OUTILS INFO MULTIMEDIA)::OIM
+Mise à Niveau::MISE A NIVEAU (EM7INFHM)::MaN
+::EM7INFHM (MISE A NIVEAU)::MaN
+Communication et Gestion::COMM. ET GESTION (EM7INFIM)::CGE
+::EM7INFIM (COMM. ET GESTION)::CGE
+Langues Vivantes Anglais::ANGLAIS (EM7INFJM)::Anglais
+::EM7INFJM (ANGLAIS)::Anglais
+Réseaux::RESEAUX (EM7ICAAM)::R
+::EM7ICAAM (RESEAUX)::R
+Conception Interactive des Systèmes Interactifs::CONCEPT. INTER. SYS INTER (EM7IHMAM)::CISI
+::EM7IHMAM (CONCEPT. INTER. SYS INTER)::CISI
+Techniques Avancées de l’Intelligence Artificielle::TECH. AVANCEE IA (EM7IARBM)::TIA
+::EM7IARBM (TECH. AVANCEE IA)::TIA
+Agents Intelligents : Raisonnement et Comportement Collectif::AGENTS INTEL. RAISON. (EM8IARJM)::AIRC2
+::EM8IARJM (AGENTS INTEL. RAISON.)::AIRC2
+Algorithmes et Structures de Données pour la Synthèse d'Images::ALGO. STRUCT. DONNEES (EM8IIMBM)::ASDSI
+::EM8IIMBM (ALGO. STRUCT. DONNEES)::ASDSI
+Architecture Logicielle::ARCH. LOGICIELLES (EM8IDLEM)::AL
+::EM8IDLEM (ARCH. LOGICIELLES)::AL
+Architecture Haute Performance::ARCHIT. HAUTE PERFOR. (EM8ICAAM)::AHP
+::EM8ICAAM (ARCHIT. HAUTE PERFOR.)::AHP
+Architectures multi-couches et développement avec Java JEE::ARCHIT. MULTI-COUCHES (EM8IDLGM)::JEE
+::EM8IDLGM (ARCHIT. MULTI-COUCHES)::JEE
+Architectures Spécialisées::ARCHIT. SPECIALISEES (EM8ICABM)::AS
+::EM8ICABM (ARCHIT. SPECIALISEES)::AS
+Bases de Données Parallèles et Réparties::BDD PARALLELES::BDPR
+::BASES DE DONNEES (MA08U05)::BDPR
+::MA08U05 (BASES DE DONNEES)::BDPR
+Conception des Systèmes répartis::CONCEPT. SYST. REPARTIS (EM8ICAFM)::CSR
+::EM8ICAFM (CONCEPT. SYST. REPARTIS)::CSR
+Développement Collaboratif et Logiciels Libres::DEV. COLL. LOG. LIBRES (EM8IDLBM)::DCLL
+::EM8IDLBM (DEV. COLL. LOG. LIBRES)::DCLL
+Gestion de projet (CMI)::GESTION DE PROJET (EM7ICMAM)::GestionProjet
+::EM7ICMAM (GESTION DE PROJET)::GestionProjet
+GRH et Communication (CMI)::GRH et Communication::GRH_Comm
+Interopérabilité et Web Services::INTEROPERABILITE APPL. (EM8IDLDM)::IAWS
+::EM8IDLDM (INTEROPERABILITE APPL.)::IAWS
+Introduction à l'Informatique Graphique 3D::INTR. INFO GRAPHE 3D (EM8IIMAM)::IG3D
+::EM8IIMAM (INTR. INFO GRAPHE 3D)::IG3D
+Introduction à l'Analyse et au Traitement d'Images::INTROD. ANA. TRAIT. IMAGE (EM8IIMCM)::IATI
+::EM8IIMCM (INTROD. ANA. TRAIT. IMAGE)::IATI
+Introduction à l'Apprentissage Automatique::INTROD. APPR. AUTO. (EM8IARFM)::IAA
+::EM8IARFM (INTROD. APPR. AUTO.)::IAA
+Introduction à la Vision par Ordinateur::INTROD. VISION ORDINATEUR (EM8IIMDM)::IVO
+::EM8IIMDM (INTROD. VISION ORDINATEUR)::IVO
+Introduction à l'Audio Numérique::INTROD. AUDIO NUMERIQUE (EM8IIMEM)::IAN
+::EM8IIMEM (INTROD. AUDIO NUMERIQUE)::IAN
+Introduction à l'Interaction Homme-Machine et à l'Usabilité de Logiciels::INTRO. INTER HM UTIL. LOG (EM8IHMGM)::IHMUL
+::EM8IHMGM (INTRO. INTER HM UTIL. LOG)::IHMUL
+Logique constructive et Sémantique des Langages de programmation::UNKNOWN12::LSL
+Management de Projets Informatiques::MANAG. DE PROJ. INFO. (EM8IDLCM)::MPI
+::EM8IDLCM (MANAG. DE PROJ. INFO.)::MPI
+Marketing et Stratégie::Marketing et Stratégie::MarketStrat
+Méthodes Agiles::METHODES AGILES (EM8IDLAM)::MA
+::EM8IDLAM (METHODES AGILES)::MA
+Modélisation, Conception et Programmation Orientées Objet - Approfondissement::MOD. CONCEPT. PROG. OO (EM8IDLFM)::MCPOOA
+::EM8IDLFM (MOD. CONCEPT. PROG. OO)::MCPOOA
+Noyaux Systèmes Temps Réel::NOYAUX SYST. TEMPS REEL (EM8ICADM)::NSTR
+::EM8ICADM (NOYAUX SYST. TEMPS REEL)::NSTR
+Optimisation de Programmes Parallèles::UNKNOWN13::OPP
+Préparation au CLES (CMI)::Prépa CLES (CMI)::CLES
+Préparation au C2i (CMI)::Prépa C2iMI (E48ICMB2)::C2i
+::E48ICMB2 (Prépa C2iMI)::C2i
+Projet de développement::PROJET DEVELOPPEMENT (EM8INFAM)::PROJET
+::EM8INFAM (PROJET DEVELOPPEMENT)::PROJET
+Recherche Opérationnelle::RECHERCHE OPERATIONNELLE (EM8IARDM)::RO
+::EM8IARDM (RECHERCHE OPERATIONNELLE)::RO
+Représentation et manipulation de contenus 3D, Image et Son::REP. MANIP. CONTENU 3D (EM8IARGM)::3DIS
+::EM8IARGM (REP. MANIP. CONTENU 3D)::3DIS
+Représentation des connaissances::REPRESENT. DES CONNAIS. (EM8IARIM)::RC
+::EM8IARIM (REPRESENT. DES CONNAIS.)::RC
+Techniques Avancées des Systèmes d'Exploitation::UNKNOWN14::TAS
+Travail Encadré de Recherche::TER (EM8INFBM)::TER
+::EM8INFBM (TER)::TER
diff --git a/data/M1INFO_GROUPS b/data/M1INFO_GROUPS
new file mode 100644
index 0000000..094b495
--- /dev/null
+++ b/data/M1INFO_GROUPS
@@ -0,0 +1,10 @@
+Groupe 1::M1_Info_1::celcat::https://edt.univ-tlse3.fr/FSI/FSIpargroupes/g1667.xml
+Groupe 2::M1_Info_2::celcat::https://edt.univ-tlse3.fr/FSI/FSIpargroupes/g1668.xml
+Groupe 3::M1_Info_3::celcat::https://edt.univ-tlse3.fr/FSI/FSIpargroupes/g1669.xml
+Groupe 4::M1_Info_4::celcat::https://edt.univ-tlse3.fr/FSI/FSIpargroupes/g1670.xml
+Groupe 5::M1_Info_5::celcat::https://edt.univ-tlse3.fr/FSI/FSIpargroupes/g1671.xml
+Groupe 6::M1_Info_6::celcat::https://edt.univ-tlse3.fr/FSI/FSIpargroupes/g1672.xml
+Groupe 7::M1_Info_7::celcat::https://edt.univ-tlse3.fr/FSI/FSIpargroupes/g1673.xml
+Groupe 8::M1_Info_8::celcat::https://edt.univ-tlse3.fr/FSI/FSIpargroupes/g1674.xml
+Groupe 9::M1_Info_9::celcat::https://edt.univ-tlse3.fr/FSI/FSIpargroupes/g1675.xml
+Groupe 10::M1_Info_10::celcat::https://edt.univ-tlse3.fr/FSI/FSIpargroupes/g1676.xml
diff --git a/data/M2CAMSI_CLASSES b/data/M2CAMSI_CLASSES
new file mode 100644
index 0000000..6f3c65f
--- /dev/null
+++ b/data/M2CAMSI_CLASSES
@@ -0,0 +1,35 @@
+Architectures Embarquées et Production de Code::UE10 : Cours Architectures Embarquées et Production de Code::AEPC
+::UE10 : Architectures Embarquées et Production de Code::AEPC
+::UE10 : Examen Architectures Embarquées et Production de Code::AEPC
+Architecture Matérielle/Logicielle des Systèmes Temps-réel::UE11 : Cours Architecture Matérielle/Logicielle des Systèmes Temps-réel::AMLST
+::UE11 : Examen Architecture Matérielle/Logicielle des Systèmes Temps-réel::AMLST
+Réseaux pour les Systèmes Temps-réel et Embarqués::UE9 : Cours Réseaux pour les Systèmes Temps-réel et Embarqués::RSTE
+::UE9 : TP Réseaux pour les Systèmes Temps-réel et Embarqués::RSTE
+::UE9 : Examen Réseaux pour les Systèmes Temps-réel et Embarqués::RSTE
+Systèmes d'Exploitation et Pilotes de périphériques::UE2 : Cours Systèmes d'Exploitation et Pilotes de périphériques::CEP
+::UE2 : TP Systèmes d'Exploitation et Pilotes de périphériques::CEP
+::UE2 : TP BE Systèmes d'exploitation et Pilotes de périphériques::CEP
+::UE2 : BE Systèmes d'exploitation et Pilotes de périphériques::CEP
+::UE2 : Examen Systèmes d'exploitation et Pilotes de périphériques::CEP
+Communication et Connaissance de l'entreprise::UE7 : Cours Communication et Connaissance de l'entreprise::CC
+::UE7 : Communication et Connaissance de l'entreprise::CC
+::UE7 : Examen Communication et Connaissance de l'entreprise::CC
+Systèmes Temps-réel et Embarqués::UE5 : Cours Systèmes Temps-réel et Embarqués::STE
+::UE5 : Systèmes Temps-réel et Embarqués::STE
+::UE5 : Examen Systèmes Temps-réel et Embarqués::STE
+Structure et Conception de Base des Machines Informatiques::UE1 : Cours Structure et Conception de Base des Machines Informatiques::SCBMI
+::UE1 : TP Structure et Conception de Base des Machines Informatiques::SCBMI
+::UE1 : Structure et Conception de Base des Machines Informatiques::SCBMI
+Langage de description du matériel : VHDL::UE0 : Cours Langage de description du matériel : VHDL::LVHDL
+::UE0 : TP Langage de description du matériel : VHDL::LVHDL
+::UE0 : Examen Langage de description du matériel : VHDL::LVHDL
+::UE0 : Examen de TP Langage de description du matériel : VHDL::LVHDL
+Anglais::UE6 : TD Anglais::Anglais
+Techniques de Conception Avancée des Machines Informatiques::UE4 : Cours Techniques de Conception Avancée des Machines Informatiques::TCAMI
+::UE4 : TP Techniques de Conception Avancée des Machines Informatiques::TCAMI
+::UE11 : Techniques de Conception Avancée des Machines Informatiques::TCAMI
+::UE11 : Conférence Continental::TCAMI
+::UE4 : Techniques de Conception Avancée des Machines Informatiques::TCAMI
+Techniques de Compilation::UE3 : Cours Techniques de Compilation::TC
+::UE3 : Examen Techniques de Compilation::TC
+Projets::UE99 : TP Projets::Projets
diff --git a/data/M2CAMSI_GROUPS b/data/M2CAMSI_GROUPS
new file mode 100644
index 0000000..1b9f312
--- /dev/null
+++ b/data/M2CAMSI_GROUPS
@@ -0,0 +1 @@
+All Students::M2CAMSI::camsi::http://planning.camsi.fr/ShowListeCreneaux.aspx
diff --git a/data/M2DL_CLASSES b/data/M2DL_CLASSES
new file mode 100644
index 0000000..4beb7e1
--- /dev/null
+++ b/data/M2DL_CLASSES
@@ -0,0 +1,131 @@
+Langue Vivante::Anglais::LV
+::TD Ue Anglais::LV
+::Cours Ue Anglais::LV
+::LV Groupe::LV
+::Réunion UE ANGLAIS::LV
+::LV G1::LV
+::LV G2::LV
+Vie des Entreprises::Intervention des entreprises::VE
+::Ue Vie des entreprises::VE
+::VE::VE
+::Cours Vie des entreprises::VE
+::Ue Vie des Entreprises::VE
+::Cours Ue Vie des Entreprises::VE
+::EXAM VE::VE
+::RAO Vie des entreprises::VE
+Ingénierie Système::IS::IS
+::TP IS::IS
+::Cours IS::IS
+::cours IS::IS
+::Cours Ue IS::IS
+::EXAM IS::IS
+::Ue IS::IS
+::Cours Ingénierie des Systèmes::IS
+Processus de l'Ingénierie Système et Modélisation de Processus::PCIS::PCIS
+::Cours Ue PCIS::PCIS
+::C Ue PCIS::PCIS
+::EXAM PCIS::PCIS
+::Cours PCIS::PCIS
+::Cours Processus de l'Ingénierie Système::PCIS
+::G2 PCIS::PCIS
+::G1 PCIS::PCIS
+Intégration, Vérification, Validation, Qualification::IVVQ::IVVQ
+::EXAM IVVQ::IVVQ
+::Soutenances IVVQ::IVVQ
+::C IVVQ::IVVQ
+::Ue IVVQ::IVVQ
+::Cours Ue IVVQ::IVVQ
+::Cours Ue Intégration Vérification Validation Qualification::IVVQ
+::Session 2 IVVQ::IVVQ
+::Cours IVVQ::IVVQ
+Outils Formels pour l'Ingénierie::OFI::OFI
+::Contrôle Terminal OFI::OFI
+::Contrôle OFI::OFI
+::Cours Ue OFI::OFI
+::Cours Ue Outils Formels pour l'Ingénierie::OFI
+::CC Ue OFI::OFI
+::TP Ue OFI::OFI
+::TP OFI::OFI
+::Exam OFI::OFI
+::Contrôle Continu Ue OFI::OFI
+::Contrôle continu OFI::OFI
+::Ue OFI::OFI
+Conception de Systèmes Ambiants::CSA::CSA
+::G1 CSA::CSA
+::G2 CSA::CSA
+Spécification Formelle des Systèmes::SFS::SFS
+::TP Spécification Formelle des Systèmes::SFS
+::TP Ue Spécification Formelle des Systèmes::SFS
+::Cours Ue Spécification Formelle des Systèmes::SFS
+::Cours Ue SFS::SFS
+::examen SFS::SFS
+::EXAM SFS::SFS
+::Exam SFS::SFS
+::Contrôle SFS::SFS
+Travaux d'Etudes et de Recherche::TER::TER
+::cours TER::TER
+::Cours TER::TER
+::Ue TER::TER
+Stage::STA::STA
+::Préparation soutenances::STA
+::Présentation des stages::STA
+::Prépa au stage::STA
+::Soutenances à confirmer::STA
+::Préparation rapport et soutenance::STA
+::Soutenances stages::STA
+::Point d'avancement sur la recherche de stages::STA
+::Présentation stages::STA
+::Point stage::STA
+::Cours Préparation au stage::STA
+::SOUTENANCES DE STAGE::STA
+::Stage::STA
+::Préparation au rapport de stage::STA
+::STAGE::STA
+::POINT STAGE::STA
+::Préparation rapport de stage::STA
+::Réunion stage::STA
+::Soutenances des stages::STA
+::Préparation du rapport de stage::STA
+::SOUTENANCES DES STAGES::STA
+::Accompagnement à la recherche de stage::STA
+Développement de Logiciels Critiques::UNKNOWN10::DLC
+Ingénierie Dirigée par les Modèles::UNKNOWN11::IDM
+Simulation de Modèles::UNKNOWN12::SM
+Validation d'Applications pour le Domaine des Transports::UNKNOWN13::VADT
+Vérification de Systèmes Réactifs::UNKNOWN14::VSR
+Conférences::UE Conférence::C
+::Conférence::C
+::Exam UE Conférence::C
+::Ue Conférence::C
+::Contrôle UE Conférences::C
+::EXAM UE CONF::C
+::Examen - UE-Conférences::C
+::Contrôle - UE-Conférences::C
+::UE CONFERENCES::C
+Programmation Concurrente et Répartie::PCR::PCR
+::EXAM PCR::PCR
+::Examen UE PCR::PCR
+::UE PCR::PCR
+::Contrôle PCR::PCR
+Processus Métier et Développement .Net::PMDD::PPMD
+::PMDN::PPMD
+::UE PMDD::PPMD
+::EXAM PMDD::PPMD
+Sécurité::SECU::SE
+::EXAM SECU::SE
+::Sécurité::SE
+::UE Sécurité::SE
+Applications Réparties à Grande Echelle::ARGE::ARGE
+::Examen UE ARGE::ARGE
+::UE ARGE::ARGE
+::EXAM ARGE::ARGE
+Systèmes Multi-Agents::SMA::SMA
+::Examen UE SMA::SMA
+::EXAM SMA::SMA
+::UE SMA::SMA
+Facteurs Humains dans la Conception d'Interfaces::FHCI::FHCI
+::Cours FHCI::FHCI
+::C FHCI::FHCI
+::Cours Ue Facteurs Humains dans la Conception d'Interfaces::FHCI
+::TP FHCI::FHCI
+::Cours Ue FHCI::FHCI
diff --git a/data/M2DL_GROUPS b/data/M2DL_GROUPS
new file mode 100644
index 0000000..e16ad70
--- /dev/null
+++ b/data/M2DL_GROUPS
@@ -0,0 +1 @@
+All Students::M2DL::lazy_ics::https://www.google.com/calendar/ical/master.developpement.logiciel%40gmail.com/public/basic.ics
diff --git a/data/M2IHM_CLASSES b/data/M2IHM_CLASSES
new file mode 100644
index 0000000..29c841f
--- /dev/null
+++ b/data/M2IHM_CLASSES
@@ -0,0 +1 @@
+None::UNKNOWN0::UNKNOWN1
diff --git a/data/M2IHM_GROUPS b/data/M2IHM_GROUPS
new file mode 100644
index 0000000..d2b43df
--- /dev/null
+++ b/data/M2IHM_GROUPS
@@ -0,0 +1 @@
+All Students::M2IHM::ics::https://www.google.com/calendar/ical/4lum4m9ols52i71dpvgnkc25r0%40group.calendar.google.com/public/basic.ics
diff --git a/data/M2IM_CLASSES b/data/M2IM_CLASSES
new file mode 100644
index 0000000..a1bca63
--- /dev/null
+++ b/data/M2IM_CLASSES
@@ -0,0 +1,23 @@
+Anglais::LANGUE VIVANTE-ANGLAIS (EI9IMMAM)::Anglais
+Gestion et communication en entreprise - Initiation à la recherche::GESTION ET COMMUNICATION (EI9IMMBM)::GCEIR
+Conception et metaprogrammation C++::CONCEPTION ET METAPROG. (EI9IMMDM)::CMC
+Outils en -ique pour l'image et le multimedia::OUTILS EN -IQUE (EI9IMMEM)::OIIM
+Traitement du signal::TRAITEMENT DU SIGNAL (EI9IMMFM)::TS
+Fondements de la synthèse d'images::SYNTHESE D'IMAGES (EI9IMMGM)::FSI
+Fondements de l'analyse d'images et de la vision par ordinateur::UNKNOWN6::FAIVO
+Méthodes de compression audio-video::METHODES DE COMPRESSION (EI9IMMIM)::MCAV
+Modélisation géométrique 3D::MODELISATION GEOM. 3D (EI9IG3AM)::MG3D
+Rendu temps réel::RENDU TEMPS REEL (EI9IG3BM)::RTR
+Traitement et analyse d’images::ANALYSE D'IMAGES (EI9IMMHM)::TAI
+Méthodes et concepts avancés pour la vision par ordinateur::UNKNOWN11::MCAVO
+Humain virtuel : du mouvement au comportement::HUMAIN VIRTUEL (EIAIMMAM)::HVMC
+Environnements virtuels interactifs et partagés::ENVIRONNEMENT VIRTUELS (EIAIG3AM)::EVIP
+Traitement de la géométrie et simulation de l’éclairage en synthèse d’images::TRAITEMENT DE LA GEOMET. (EIAIG3BM)::TGSESI
+Reconnaissance des formes et interprétation d’images::UNKNOWN15::RFIA
+Imagerie médicale et spatiale::IMAGERIE MEDICALE (EIAIANBM)::IMS
+Reconnaissance des personnes dans les enregistrements audiovisuels::UNKNOWN17::RPEA
+Game design::UNKNOWN18::GD
+Algorithmique des architectures spécialisées pour l’image et le multimedia::UNKNOWN19::AASIM
+Traitement automatique parole et musique::UNKNOWN20::TAPM
+Analyse et synthèse des environnements sonores::UNKNOWN21::ASES
+Réalité augmentée::UNKNOWN22::RA
diff --git a/data/M2IM_GROUPS b/data/M2IM_GROUPS
new file mode 100644
index 0000000..983ecf7
--- /dev/null
+++ b/data/M2IM_GROUPS
@@ -0,0 +1 @@
+All Students::M2IM::celcat::https://edt.univ-tlse3.fr/FSI/FSIpargroupes/g1128.xml
diff --git a/data/M2RIT_IAICI_CLASSES b/data/M2RIT_IAICI_CLASSES
new file mode 100644
index 0000000..17e2861
--- /dev/null
+++ b/data/M2RIT_IAICI_CLASSES
@@ -0,0 +1,30 @@
+Anglais::ANGLAIS (ER9INTBM)::Anglais
+::[BONUS][Anglais]::Anglais
+Apprentissage::APPRENTISSAGE (ER9IAIAM)::AP
+::[BONUS][AP]::AP
+Systèmes multi-agents::SYST. MULTI-AGENTS (ER9IAIBM)::SMA
+::[BONUS][SMA]::SMA
+Systèmes autonomes auto-adaptatifs::SYST. AUTON. AUTO-ADAPT. (ER9IAICM)::SAAA
+::[BONUS][SAAA]::SAAA
+Modèles et algorithmes pour les problèmes d'optimisation complexes de l'IA::MOD. ALGO. PB. OPT.COM.IA (ER9IAIEM)::MPOSC
+::[BONUS][MPOSC]::MPOSC
+Simulation de systèmes complexes::SIMULATION SYST. COMPLEXE (ER9IAIFM)::SSC
+::[BONUS][SSC]::SSC
+Planification et décision::PLANIFICATION ET DECISION (ER9IAIHM)::PLD
+::[BONUS][PLD]::PLD
+Modèles probabilistes et algorithmes pour la bio-informatique::MOD. PROBA. ALGO. BIO-INF (ER9IAIJM)::AMB
+::[BONUS][AMB]::AMB
+Logique du premier ordre et types de raisonnement::LOG. 1er ORDRE\, TYPES RAI (ER9IAIKM)::LPOTR
+::[BONUS][LPOTR]::LPOTR
+Modèles logiques d'agents et de leur interaction::MOD. LOG. AGENTS INTERAC. (ER9IAILM)::MLAI
+::[BONUS][MLAI]::MLAI
+Techniques fondamentales pour le traitement automatique du langage et applications::TECH. FOND. TRAIT. A.L.A. (ER9IAIDM)::TAL
+::[BONUS][TAL]::TAL
+Systèmes coopératifs::UNKNOWN10::SC
+::[BONUS][SC]::SC
+Représentation et traitement de l'incertitude::REPRESENT. TRAIT. INCERT. (ER9IAIIM)::RTI
+::[BONUS][RTI]::RTI
+Fondements de l'interaction et de la coopération::UNKNOWN12::FIE
+::[BONUS][FIE]::FIE
+[BONUS] Seminaires Toulousains::S�minaire::BONUS_ST
+::[BONUS][SemToulouse]::BONUS_ST
diff --git a/data/M2RIT_IAICI_GROUPS b/data/M2RIT_IAICI_GROUPS
new file mode 100644
index 0000000..3a2e723
--- /dev/null
+++ b/data/M2RIT_IAICI_GROUPS
@@ -0,0 +1 @@
+All Students::M2RIT_IAICI::celcat::https://celcatfsi.ups-tlse.fr/FSIvetsem/g985.xml::lazy_ics::http://www.laas.fr/template/laas-cnrs-2009/support/protected/modules/seminaires/seminaires/calendrier/seminaires_laas.ics::lazy_ics::https://www.multiagentsystems.org/calendar/seminaires_toulouse.ics::celcat::https://edt.univ-tlse3.fr/FSI/FSIpargroupes/g2090.xml::lazy_ics::https://www.multiagentsystems.org/calendar/iaici_from_email_or_website.ics
diff --git a/data/M2RIT_RIBDM_CLASSES b/data/M2RIT_RIBDM_CLASSES
new file mode 100644
index 0000000..a75c287
--- /dev/null
+++ b/data/M2RIT_RIBDM_CLASSES
@@ -0,0 +1,21 @@
+Anglais::ANGLAIS (ER9INTBM)::Anglais
+::[BONUS] Anglais::Anglais
+Modèles de recherche d'information::MOD. RECHERCHE INFORMAT° (ER9IRIAM)::MRI
+Médiation de sources de données hétérogènes::UNKNOWN3::MSDH
+Analyses exploratoires multidimensionnelles et visualisations::UNKNOWN4::AEMV
+Bases de données multidimensionnelles::BD MULTIDIMENSIONNELLES (ER9IRIDM)::BDM
+Accès à l'information sur le web::ACCES INFORMATION WEB (ER9IRIEM)::AIW
+Bases de données réparties et mobiles::BD REPARTIES MOBILES (ER9IRIFM)::BDRM
+Ontologies et Web sémantique::ONTOLOGIES WEB SEMANT. (ER9IRIGM)::OWS
+Requêtes multimédia::UNKNOWN9::RM
+Recherche de contenus multimédia::UNKOWN10::RCM
+Classification et reconnaissance de formes::UNKNOWN11::CRF
+Fondements de l'analyse d'images::UNKNOWN12::FAI
+Analyse, codage et indexation de la vidéo::UNKNOWN13::ACIV
+Audio numérique : Parole et musique::AUDIONUMERIQUE MIT RIBDM::ANPM
+::NEII5D - TP - Audionumérique : parole et musique::ANPM
+::NEII5C::ANPM
+Humain virtuel, du mouvement au comportement::UNKNOWN15::HVMC
+Fondements de la synthèse d'images::UNKNOWN16::FSI
+[BONUS] Seminaires Toulousains::S�minaire::BONUS_ST
+::[BONUS][SemToulouse]::BONUS_ST
diff --git a/data/M2RIT_RIBDM_GROUPS b/data/M2RIT_RIBDM_GROUPS
new file mode 100644
index 0000000..f6ea790
--- /dev/null
+++ b/data/M2RIT_RIBDM_GROUPS
@@ -0,0 +1 @@
+All Students::M2RIT_RIBDM::lazy_ics::https://edt.inp-toulouse.fr/jsp/custom/modules/plannings/anonymous_cal.jsp?resources=2748&projectId=1&calType=ical&nbWeeks=20::celcat::https://edt.univ-tlse3.fr/FSI/FSIpargroupes/g2091.xml::lazy_ics::http://www.laas.fr/template/laas-cnrs-2009/support/protected/modules/seminaires/seminaires/calendrier/seminaires_laas.ics::lazy_ics::https://www.multiagentsystems.org/calendar/seminaires_toulouse.ics
diff --git a/data/M2RIT_RO_CLASSES b/data/M2RIT_RO_CLASSES
new file mode 100644
index 0000000..1c4e5b0
--- /dev/null
+++ b/data/M2RIT_RO_CLASSES
@@ -0,0 +1,78 @@
+Anglais::Anglais::Anglais
+::Anglais (que pour les inscrits UPS)::Anglais
+::Anglais (?)::Anglais
+::Anglais ?::Anglais
+::Rattrapage 2 - 2014-2015 : Anglais::Anglais
+Fondamentaux de la RO::Fond. de la RO::FRO
+::TP Fond. de la RO : groupe A::FRO
+::TP Fond. de la RO : groupe B::FRO
+::TP Fond. de la RO::FRO
+::EXAMEN Fond. de la RO::FRO
+::TP groupe 1 Fond. de la RO::FRO
+::TP groupe 2 Fond. de la RO::FRO
+::Examen rattrapage 2014-2015 Fond. de la RO::FRO
+Graphes et réseaux : modélisation et algorithmes::Graphes et r�seaux : mod�lisation et algorithmes::GRMA
+::Graphes::GRMA
+::EXAMEN Graphes::GRMA
+::Graphes : S�ance report�e � une date ult�rieure::GRMA
+Optimisation numérique locale::Opti num locale::ONL
+::TD Opti num locale Groupe B::ONL
+::TD Opti num locale Groupe A::ONL
+::TD Opti num locale::ONL
+::TP Opti num locale::ONL
+::TD A Opti num locale::ONL
+::TD B Opti num locale::ONL
+::TP A Opti num locale::ONL
+::TP B Opti num locale::ONL
+::EXAMEN : Opti num locale::ONL
+::EXAMEN Opti num locale::ONL
+::EXAMEN 1 - Opti num locale::ONL
+Bases de l’optimisation combinatoire::TP Bases Opti combin::BOC
+::Bases Opti combin::BOC
+::Bases Opti combin EXAMEN::BOC
+::EXAMEN Bases Opti combin::BOC
+Optimisation combinatoire avancée::Opti Combin Av::OCA
+::EXAMEN de rattrapage Opti Combin Av::OCA
+::EXAMEN Opti Combin Av::OCA
+Introduction à l’optimisation et la commande optimale::Introduction � l'optimisation et la commande optimale::IOCO
+::Intro Opti Commande::IOCO
+::Intro Opti Commande : � Optimisation stochastique �::IOCO
+::EXAMEN Intro Opti Commande : � Optimisation stochastique �::IOCO
+Méthodes stochastiques pour l'optimisation globale::M�thodes Sto::MSOG
+::TD M�thodes Sto::MSOG
+::TP M�thodes Sto::MSOG
+::EXAMEN M�thodes Sto::MSOG
+::EXAMEN TP M�thodes Sto::MSOG
+::EXAMEN de RATTRAPAGE - M�thodes Sto::MSOG
+Production et logistique::Prod & Logis::PL
+::BE Prod & Logis::PL
+::EXAMEN Prod & Logis::PL
+::EXAMEN : Prod & Logis::PL
+::EXAMEN - Prod & Logis::PL
+::EXAMEN rattrapage 2014-2015 Prod & Logis::PL
+Optimisation sous incertitude::Optimisation sous incertitude::OSI
+::Opti Incertitude::OSI
+::Examen Opti Incertitude::OSI
+::EXAMEN Opti Incertitude::OSI
+::Examen rattrapage 2014-2015 : Opti Incertitude::OSI
+Optimisation globale déterministe::Opti Globale D�t::OGD
+::EXAMEN Opti Globale D�t::OGD
+::TP Opti Globale D�t::OGD
+::S�ance report�e � une date ult�rieure - Opti Globale D�t::OGD
+Applications aux transports et à l'aéronautique::Appli A�ro::ATA
+::Examen Appli A�ro::ATA
+::EXAMEN Appli A�ro::ATA
+::TD Appli A�ro::ATA
+::EXAMEN de RATTRAPAGE - Appli A�ro::ATA
+::TP Appli A�ro::ATA
+Formation d'appoint en aéronautique::Formation d appoint en a�ronautique::FAA
+::Formation d appoint en a�ronautiqueo::FAA
+Complexité des algorithmes::Complexit�::CA
+::EXAMEN Complexit�::CA
+::EXAMEN - Complexit�::CA
+::EXAMEN : Complexit�::CA
+Optimisation et Recherche Opérationnelle (2012-2013)::Optimisation et Recherche op�rationnelle::ORO
+::Opti & RO::ORO
+::TP Opti & RO::ORO
+[BONUS] Seminaires Toulousains::S�minaire::BONUS_ST
+::[BONUS][SemToulouse]::BONUS_ST
diff --git a/data/M2RIT_RO_GROUPS b/data/M2RIT_RO_GROUPS
new file mode 100644
index 0000000..790d0de
--- /dev/null
+++ b/data/M2RIT_RO_GROUPS
@@ -0,0 +1 @@
+All Students::M2RIT_RO::dhx_cal::http://m2rit-ro.recherche.enac.fr/calendrier/php/events.php::lazy_ics::http://www.laas.fr/template/laas-cnrs-2009/support/protected/modules/seminaires/seminaires/calendrier/seminaires_laas.ics::lazy_ics::https://www.multiagentsystems.org/calendar/seminaires_toulouse.ics
diff --git a/data/M2RIT_RT_CLASSES b/data/M2RIT_RT_CLASSES
new file mode 100644
index 0000000..913b042
--- /dev/null
+++ b/data/M2RIT_RT_CLASSES
@@ -0,0 +1,36 @@
+Anglais::CM Anglais::Anglais
+::ANGLAIS (ER9INTBM)::Anglais
+Algorithmes et protocoles de gestion de la qualité de service::Algorithme et protocole de gestion::APGQS
+::EXAMEN Algorithme et protocole de gestion::APGQS
+::ALGO. MECA. GEST. QUAL. S (ER9IRTFM)::APGQS
+Techniques et protocoles de sécurité dans les réseaux et systèmes d'exploitation::UNKNOWN1::TPSRSE
+Architectures et protocoles de gestion de la QoS dans l'Internet::UNKNOWN2::APGQSI
+Composition de services pour le multimédia dans l'Internet::UNKNOWN3::CSMI
+Réseaux opérateurs haut débit::NI1E22::ROHD
+::NI1E24::ROHD
+Réseaux embarqués::Réseaux Embarqués::RE
+::EXAMEN Réseaux Embarqués::RE
+Développement d’applications réparties::UNKNOWN6::DAR
+Modélisation et évaluation de performance des réseaux::UNKNOWN7::MEPR
+Gestion de réseaux et de services::NETG3C::GRS
+Modèles et politiques de sécurité::UNKNOWN9::MPS
+Réseaux sans fil et mobiles::NETG2C::RSFM
+::NETG2B::RSFM
+Modulations numériques avancées::NETG1D::MNA
+Communications aéronautiques::UNKNOWN12::CA
+Egalisation et modèles de canaux::UNKNOWN13::EMC
+Récepteurs numériques::UNKNOWN14::RN
+Accès multiple par codage et détection multi-utilisateurs::UNKNOWN15::AMCDMU
+Compression du signal::UNKNOWN16::CS
+Techniques multi-porteuses::UNKNOWN17::TMP
+Systèmes de communication mobiles terrestres::NETG1C::SCMT
+Télécommunications par satellite::UNKNOWN19::TPS
+Diffusion et multimédia par satellite::UNKNOWN20::DMS
+Architectures et protocoles de l'Internet::Architecture et protocole de l'Internet::API
+Déploiement de l'Internet et interconnexion avancée::Déploiement de l'Internet et interconnexion avancée::DIIA
+Principes des communications::Principe des Communications::PC
+::EXAMEN Principe des Communications::PC
+Techniques de communications pour les mobiles::UNKNOWN23::TCM
+Modélisation et processus stochastiques::UNKNOWN24::MPSt
+[BONUS] Seminaires Toulousains::S�minaire::BONUS_ST
+::[BONUS][SemToulouse]::BONUS_ST
diff --git a/data/M2RIT_RT_GROUPS b/data/M2RIT_RT_GROUPS
new file mode 100644
index 0000000..54dd30c
--- /dev/null
+++ b/data/M2RIT_RT_GROUPS
@@ -0,0 +1 @@
+All Students::M2RIT_RT::lazy_ics::https://edt.inp-toulouse.fr/jsp/custom/modules/plannings/anonymous_cal.jsp?resources=20&projectId=1&calType=ical&nbWeeks=20::celcat::https://edt.univ-tlse3.fr/FSI/FSIpargroupes/g2094.xml::lazy_ics::http://www.laas.fr/template/laas-cnrs-2009/support/protected/modules/seminaires/seminaires/calendrier/seminaires_laas.ics::lazy_ics::https://www.multiagentsystems.org/calendar/seminaires_toulouse.ics
diff --git a/data/M2RIT_SRLC_CLASSES b/data/M2RIT_SRLC_CLASSES
new file mode 100644
index 0000000..7b952ed
--- /dev/null
+++ b/data/M2RIT_SRLC_CLASSES
@@ -0,0 +1,28 @@
+Anglais::ANGLAIS (ER9INTBM)::Anglais
+::[BONUS] Anglais::Anglais
+Complexité des algorithmes::CTD Complexité des algorithmes::CA
+Algorithmique et systèmes répartis::NEI2A - CM - Systèmes et algorithmes répartis::ASR
+::NEI2A - EXAM - Systèmes et algorithmes répartis::ASR
+Intergiciels::NEIL5A - TP - Systèmes avancés::I
+::NEIL5A - EXAM - Systèmes avancés::I
+::NEIL5A - CM - Systèmes avancés::I
+::NEIL5A - TPA - Systèmes avancés::I
+::NEIL5A - TPB - Systèmes avancés::I
+Calcul réparti et Grid computing::NEI2B - CM - Calcul réparti et grid computing::CRGC
+::NEI2B - EXAM - Calcul réparti et grid computing::CRGC
+Modèles d’exécution et architectures parallèles::UNKNOWN4::MEAP
+Architectures : parallélisme et systèmes critiques::UNKNOWN5::APSC
+Conception de systèmes temps réel::UNKNOWN6::CSTR
+Conception conjointe de systèmes temps réel et langages (a)synchrones::UNKNOWN7::CCSTRLA
+Ordonnancement dans les systèmes temps réel et embarqués::UNKNOWN8::OSTRE
+Ingénierie des modèles::UNKNOWN9::IM
+Modèle d'ingénierie du processus de développement::UNKNOWN10::MIPD
+Spécification formelle des systèmes::UNKNOWN11::SFS
+Vérification de systèmes réactifs::UNKNOWN12::VSR
+Validation par analyse statique::NEIL4A - CM -Vérification par analyse statique::VAS
+::NEIL4B - BE -Vérification par analyse statique::VAS
+::NEIL4B - CM -Vérification par analyse statique::VAS
+Techniques de compilation::UNKNOWN14::TC
+Théorie des types et applications::UNKNOWN15::TTA
+[BONUS] Seminaires Toulousains::S�minaire::BONUS_ST
+::[BONUS][SemToulouse]::BONUS_ST
diff --git a/data/M2RIT_SRLC_GROUPS b/data/M2RIT_SRLC_GROUPS
new file mode 100644
index 0000000..34bd671
--- /dev/null
+++ b/data/M2RIT_SRLC_GROUPS
@@ -0,0 +1 @@
+All Students::M2RIT_SRLC::ics::https://edt.inp-toulouse.fr/jsp/custom/modules/plannings/anonymous_cal.jsp?resources=15&projectId=1&calType=ical&nbWeeks=20::celcat::https://edt.univ-tlse3.fr/FSI/FSIpargroupes/g2092.xml::lazy_ics::http://www.laas.fr/template/laas-cnrs-2009/support/protected/modules/seminaires/seminaires/calendrier/seminaires_laas.ics::lazy_ics::https://www.multiagentsystems.org/calendar/seminaires_toulouse.ics
diff --git a/src/CAMSIParser.java b/src/CAMSIParser.java
new file mode 100644
index 0000000..b074b16
--- /dev/null
+++ b/src/CAMSIParser.java
@@ -0,0 +1,125 @@
+import java.util.TimeZone;
+import java.util.Calendar;
+import java.util.Locale;
+
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+import java.io.BufferedReader;
+
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.stream.XMLStreamException;
+
+/**
+ * This class stores the incoming data from XML.
+ * It is needed because of the stream nature of the XML parser.
+ **/
+public class CAMSIParser
+{
+ final static Pattern event_pattern;
+
+ static
+ {
+ event_pattern = Pattern.compile
+ (
+ "[^<]*<td><[^>]*>([0-9]+)/([0-9]+)/([0-9]+)</[^>]*></td>"
+ + "<td><[^>]*>([0-9]+):([0-9]+)</[^>]*></td>"
+ + "<td><[^>]*>([0-9]+):([0-9]+)</[^>]*></td>"
+ + "<td><[^>]*>([^<]*)</[^>]*></td>" // 8th
+ + "<td><[^>]*>([^<]*)</[^>]*></td>"
+ + "<td><[^>]*>([^<]*)</[^>]*></td>"
+ );
+ }
+
+ public static void parse
+ (
+ final BufferedReader reader,
+ final Group group,
+ final String uid_prefix
+ )
+ throws Exception
+ {
+ int uid;
+ String line;
+ Matcher matcher;
+
+ uid = 0;
+
+ while (reader.ready())
+ {
+ line = reader.readLine();
+ matcher = event_pattern.matcher(line);
+
+ if (matcher.matches())
+ {
+ final Event e;
+ final java.util.Calendar c_start, c_end;
+
+ c_start = java.util.Calendar.getInstance
+ (
+ TimeZone.getTimeZone("Europe/Paris"),
+ Locale.FRENCH
+ );
+
+ c_start.set
+ (
+ java.util.Calendar.DAY_OF_MONTH,
+ Integer.parseInt(matcher.group(1))
+ );
+
+ c_start.set
+ (
+ java.util.Calendar.MONTH,
+ /* It's Java... don't question it. */
+ (Integer.parseInt(matcher.group(2)) - 1)
+ );
+
+ c_start.set
+ (
+ java.util.Calendar.YEAR,
+ Integer.parseInt(matcher.group(3))
+ );
+
+ c_start.set
+ (
+ java.util.Calendar.HOUR_OF_DAY,
+ Integer.parseInt(matcher.group(4))
+ );
+
+ c_start.set
+ (
+ java.util.Calendar.MINUTE,
+ Integer.parseInt(matcher.group(5))
+ );
+
+
+ c_end = (java.util.Calendar) c_start.clone();
+
+ c_end.set
+ (
+ java.util.Calendar.HOUR_OF_DAY,
+ Integer.parseInt(matcher.group(6))
+ );
+
+ c_end.set
+ (
+ java.util.Calendar.MINUTE,
+ Integer.parseInt(matcher.group(7))
+ );
+
+ e = new Event();
+
+ e.set_start_time(c_start);
+ e.set_end_time(c_end);
+ e.set_name(matcher.group(8));
+ e.add_speaker(matcher.group(9));
+ e.add_location(matcher.group(10));
+ e.set_uid(uid_prefix + (uid++));
+
+ group.add_event(e);
+ }
+ }
+
+ reader.close();
+ }
+}
diff --git a/src/Calendar.java b/src/Calendar.java
new file mode 100644
index 0000000..18c097d
--- /dev/null
+++ b/src/Calendar.java
@@ -0,0 +1,39 @@
+import java.util.ArrayList;
+import java.util.List;
+import java.util.HashMap;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.InputStreamReader;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+
+public class Calendar
+{
+ private Calendar () {} /* Utility class */
+
+ public static void main (final String[] args)
+ {
+ if (!Parameters.set_parameters(args))
+ {
+ /**
+ * Printed usage.
+ **/
+ return;
+ }
+
+ Error.init_handler();
+
+ try
+ {
+ Classes.read_all();
+ Group.read_all();
+ Group.run_all();
+ }
+ catch (final Exception e) {}
+
+ Error.finalize_handler();
+ }
+}
diff --git a/src/CelcatParser.java b/src/CelcatParser.java
new file mode 100644
index 0000000..01e0314
--- /dev/null
+++ b/src/CelcatParser.java
@@ -0,0 +1,482 @@
+import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Calendar;
+import java.util.Locale;
+import java.util.Iterator;
+import java.util.TimeZone;
+
+import java.io.PrintWriter;
+
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.stream.XMLStreamException;
+
+/**
+ * This class stores the incoming data from XML.
+ * It is needed because of the stream nature of the XML parser.
+ **/
+public class CelcatParser
+{
+ private final HashMap<Integer, Calendar> weeks;
+ private final Group group;
+ private String day;
+ private String week;
+ private String start_time;
+ private String end_time;
+ private String uid_prefix;
+ private Event event;
+ private DataType data_type;
+ private ListType current_list_type;
+ private int event_count;
+
+ public static void parse
+ (
+ final XMLStreamReader stream_reader,
+ final Group group,
+ final String uid_prefix
+ )
+ throws XMLStreamException
+ {
+ final CelcatParser parser;
+
+ parser = new CelcatParser(group, uid_prefix);
+
+ while (stream_reader.hasNext())
+ {
+ switch (stream_reader.next())
+ {
+ case XMLStreamReader.START_ELEMENT:
+ parser.handle_new_element(stream_reader);
+ break;
+
+ case XMLStreamReader.CDATA:
+ case XMLStreamReader.CHARACTERS:
+ parser.handle_data(stream_reader.getText().trim());
+ break;
+
+ case XMLStreamReader.END_DOCUMENT:
+ parser.flush();
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ private CelcatParser
+ (
+ final Group group,
+ final String uid_prefix
+ )
+ {
+ this.group = group;
+ this.uid_prefix = uid_prefix;
+
+ event = null;
+ event_count = 0;
+ weeks = new HashMap<Integer, Calendar>();
+ }
+
+ /**
+ * Called when entering an new element.
+ **/
+ private void handle_new_element (final XMLStreamReader stream_reader)
+ {
+ final DataType new_data_type;
+
+ new_data_type = DataType.get(stream_reader.getLocalName());
+
+ switch (new_data_type)
+ {
+ case EVENT:
+ /** We're done handling the previous event. **/
+ flush();
+ break;
+
+ case SPAN:
+ /** This contains information about the weeks. **/
+ weeks.put
+ (
+ Integer.parseInt
+ (
+ stream_reader.getAttributeValue(null, "rawix")
+ ),
+ get_calendar_from_span
+ (
+ stream_reader.getAttributeValue(null, "date")
+ )
+ );
+ break;
+
+ case ROOM:
+ /**
+ * Multiple rooms may be concerned, hence the List.
+ * Any following ITEM elements should be considered as describing
+ * a room for this event.
+ **/
+ current_list_type = ListType.ROOMS;
+ break;
+
+ case GROUP:
+ /**
+ * Multiple groups may be concerned, hence the List.
+ * Any following ITEM elements should be considered as describing
+ * a group for this event.
+ **/
+ current_list_type = ListType.GROUPS;
+ break;
+
+ case ITEM:
+ if (data_type == DataType.MODULE)
+ {
+ /**
+ * The XML file will consider that the event may concern
+ * multiple modules. It makes sense, but I've never seen
+ * it happen and it complicates the process.
+ * Not updating the data_type here will make the next
+ * data be considered as the Module's name instead of
+ * something to add to a list.
+ **/
+
+ /* [XXX][Improvement] Handle it. */
+
+ return;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ data_type = new_data_type;
+ }
+
+ private void flush ()
+ {
+ week = null;
+ start_time = null;
+ end_time = null;
+
+ if (event == null)
+ {
+ /**
+ * This is the first event, we don't have anything to write.
+ **/
+
+ event = new Event();
+ event.set_uid(uid_prefix + (event_count++));
+
+ return;
+ }
+
+ group.add_event(event);
+
+ event = new Event();
+ event.set_uid(uid_prefix + (event_count++));
+
+ current_list_type = ListType.IGNORED;
+ }
+
+ /**
+ * Called when finding actual data.
+ **/
+ private void handle_data (final String data)
+ {
+ if (data.equals(""))
+ {
+ /**
+ * Data was garbage.
+ * It happens sometimes, I don't know why.
+ **/
+
+ /* [XXX][Improvement] Prevent this from happening. */
+ return;
+ }
+
+ /**
+ * Uses the previously set "data_type" to know where the data belongs.
+ **/
+ switch (data_type)
+ {
+ case MODULE:
+ event.set_name(data);
+ break;
+
+ case STARTTIME:
+ if ((week == null) || (day == null))
+ {
+ start_time = data;
+ }
+ else
+ {
+ event.set_start_time
+ (
+ get_calendar
+ (
+ week_to_calendar(week),
+ day,
+ data
+ )
+ );
+ }
+ break;
+
+ case ENDTIME:
+ if ((week == null) || (day == null))
+ {
+ end_time = data;
+ }
+ else
+ {
+ event.set_end_time
+ (
+ get_calendar
+ (
+ week_to_calendar(week),
+ day,
+ data
+ )
+ );
+ }
+ break;
+
+ case NOTES:
+ if (event != null)
+ {
+ event.set_description("[Notes] " + data);
+ }
+ break;
+
+ case RAWWEEKS:
+ week = data;
+
+ if ((start_time != null) && (day != null))
+ {
+ event.set_start_time
+ (
+ get_calendar
+ (
+ week_to_calendar(week),
+ day,
+ start_time
+ )
+ );
+ }
+
+ if ((end_time != null) && (day != null))
+ {
+ event.set_end_time
+ (
+ get_calendar
+ (
+ week_to_calendar(week),
+ day,
+ end_time
+ )
+ );
+ }
+
+ break;
+
+ case DAY:
+ day = data;
+
+ if ((start_time != null) && (week != null))
+ {
+ event.set_start_time
+ (
+ get_calendar
+ (
+ week_to_calendar(week),
+ day,
+ start_time
+ )
+ );
+ }
+
+ if ((end_time != null) && (week != null))
+ {
+ event.set_end_time
+ (
+ get_calendar
+ (
+ week_to_calendar(week),
+ day,
+ end_time
+ )
+ );
+ }
+ break;
+
+ case CATEGORY:
+ event.set_category(data);
+ break;
+
+ case ITEM:
+ switch (current_list_type)
+ {
+ case ROOMS:
+ event.add_location(data);
+ break;
+
+ case GROUPS:
+ event.add_attendee(data);
+ break;
+
+ default:
+ break;
+ }
+ break;
+ }
+ }
+
+ /**
+ * Retrieves the relevant data from a week description.
+ **/
+ private Calendar get_calendar_from_span (final String spandata)
+ {
+ final String[] data;
+ final Calendar result;
+
+ data = spandata.split("/");
+
+ result =
+ java.util.Calendar.getInstance
+ (
+ TimeZone.getTimeZone("Europe/Paris"),
+ Locale.FRENCH
+ );
+
+ if (data.length != 3)
+ {
+ Error.ERROR.from_thread
+ (
+ group.get_abbreviation(),
+ ("Invalid date format for week:" + spandata)
+ );
+
+ return null;
+ }
+
+ result.set(java.util.Calendar.DAY_OF_MONTH, Integer.parseInt(data[0]));
+
+ /* Java... don't ask... */
+ result.set(java.util.Calendar.MONTH, Integer.parseInt(data[1]) - 1);
+
+ result.set(java.util.Calendar.YEAR, Integer.parseInt(data[2]));
+
+ return result;
+ }
+
+
+ /**
+ * Retrieves the week relevant to the event being processed.
+ **/
+ private Calendar week_to_calendar
+ (
+ final String week
+ )
+ {
+ final java.util.Calendar result;
+ int pos;
+
+ for (pos = 0; week.charAt(pos) != 'Y'; ++pos);
+
+ result = weeks.get(pos + 1);
+
+ if (result == null)
+ {
+ Error.ERROR.from_thread
+ (
+ group.get_abbreviation(),
+ "An event appeared before its week description did."
+ );
+ }
+
+ return result;
+ }
+
+ private java.util.Calendar get_calendar
+ (
+ final Calendar week,
+ final String day,
+ final String hourmin
+ )
+ {
+ final String[] time;
+ final java.util.Calendar result;
+
+ time = hourmin.split(":");
+
+ result = (java.util.Calendar) week.clone();
+
+ result.set(java.util.Calendar.HOUR_OF_DAY, Integer.parseInt(time[0]));
+ result.set(java.util.Calendar.MINUTE, Integer.parseInt(time[1]));
+ result.add(java.util.Calendar.DAY_OF_YEAR, Integer.parseInt(day));
+
+ return result;
+ }
+
+ private static enum ListType
+ {
+ IGNORED, ROOMS, GROUPS;
+ }
+
+ /**
+ * This enum is used to sort what is relevant in the XML file.
+ * Sadly, for justified reasons, Java does not permit access to the static
+ * members of an enum from its constructor, hence the "static"'s redundancy.
+ **/
+ private static enum DataType
+ {
+ IRRELEVANT,
+ EVENT,
+ DAY,
+ STARTTIME,
+ ENDTIME,
+ CATEGORY,
+ RAWWEEKS,
+ ITEM,
+ MODULE,
+ ROOM,
+ SPAN,
+ NOTES,
+ GROUP;
+
+ private static final HashMap<String, DataType> FROM_STRING;
+
+ static
+ {
+ FROM_STRING = new HashMap<String, DataType>();
+
+ FROM_STRING.put("irrelevant", IRRELEVANT);
+ FROM_STRING.put("event", EVENT);
+ FROM_STRING.put("day", DAY);
+ FROM_STRING.put("starttime", STARTTIME);
+ FROM_STRING.put("endtime", ENDTIME);
+ FROM_STRING.put("category", CATEGORY);
+ FROM_STRING.put("item", ITEM);
+ FROM_STRING.put("module", MODULE);
+ FROM_STRING.put("room", ROOM);
+ FROM_STRING.put("group", GROUP);
+ FROM_STRING.put("rawweeks", RAWWEEKS);
+ FROM_STRING.put("span", SPAN);
+ FROM_STRING.put("notes", NOTES);
+ }
+
+ public static DataType get (final String e)
+ {
+ final DataType result;
+
+ result = FROM_STRING.get(e);
+
+ if (result == null)
+ {
+ return IRRELEVANT;
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/src/Classes.java b/src/Classes.java
new file mode 100644
index 0000000..31cc0fa
--- /dev/null
+++ b/src/Classes.java
@@ -0,0 +1,341 @@
+import java.util.List;
+import java.util.Set;
+import java.util.HashMap;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.InputStreamReader;
+
+public class Classes
+{
+ private static final HashMap<String, Classes> FROM_ABBREVIATION;
+ private static final HashMap<String, Classes> FROM_NAME;
+ private static final String SEPARATOR = " ,.?-&:;";
+
+ private final String name;
+ private final String abbreviation;
+
+ static
+ {
+ FROM_ABBREVIATION = new HashMap<String, Classes>();
+ FROM_NAME = new HashMap<String, Classes>();
+ }
+
+ private Classes (final String name, final String abbreviation)
+ {
+ this.name = name;
+ this.abbreviation = abbreviation;
+ }
+
+ private void register ()
+ throws Classes.AlreadyRegisteredException
+ {
+ /**
+ * We allow multiple (unique) names for the same class, but not
+ * multiple abbreviations
+ **/
+
+ if (FROM_NAME.containsKey(name))
+ {
+ throw (new Classes.AlreadyRegisteredException());
+ }
+
+ if (!FROM_ABBREVIATION.containsKey(abbreviation))
+ {
+ FROM_ABBREVIATION.put(abbreviation, this);
+ }
+
+ FROM_NAME.put(name, this);
+ }
+
+ @Override
+ public boolean equals (Object a)
+ {
+ if (a instanceof Classes)
+ {
+ return abbreviation.equals(((Classes) a).abbreviation);
+ }
+
+ return false;
+ }
+
+ @Override
+ public String toString ()
+ {
+ return abbreviation;
+ }
+
+ public static String name_from_abbreviation (final String abbr)
+ {
+ final Classes c;
+
+ c = FROM_ABBREVIATION.get(abbr);
+
+ if (c == null)
+ {
+ return "unknown";
+ }
+
+ return c.name;
+ }
+
+ private static boolean word_is_isolated
+ (
+ final String word,
+ final String sentence
+ )
+ {
+ final int index_start, index_end;
+
+ index_start = sentence.indexOf(word);
+
+ if
+ (
+ (index_start == -1)
+ || (index_start == 0)
+ || (SEPARATOR.indexOf(sentence.charAt(index_start - 1)) == -1)
+ )
+ {
+ return false;
+ }
+
+ index_end = (index_start + word.length());
+
+ if (index_end == sentence.length())
+ {
+ return true;
+ }
+
+ return (SEPARATOR.indexOf(sentence.charAt(index_end)) != -1);
+ }
+
+ private static Classes ambiguous_entry_guard
+ (
+ final String entry,
+ final String class_name
+ )
+ {
+ final String rest_of_entry_name;
+ boolean ambiguous;
+
+ ambiguous = false;
+ rest_of_entry_name = entry.substring(class_name.length());
+
+ for (final String other_class_name: FROM_NAME.keySet())
+ {
+ if
+ (
+ !other_class_name.equals(class_name)
+ && word_is_isolated(other_class_name, rest_of_entry_name)
+ && !FROM_NAME.get(class_name).equals
+ (
+ FROM_NAME.get(other_class_name)
+ )
+ )
+ {
+ final StringBuilder sb;
+
+ sb = new StringBuilder();
+
+ sb.append("Ambiguous event summary \"");
+ sb.append(entry);
+ sb.append("\". Starts with \"");
+ sb.append(class_name);
+ sb.append("\" (");
+ sb.append(FROM_NAME.get(class_name));
+ sb.append("), but also contains \"");
+ sb.append(other_class_name);
+ sb.append("\" (");
+ sb.append(FROM_NAME.get(other_class_name));
+ sb.append(")");
+
+ Error.WARNING.from_file
+ (
+ Parameters.get_known_classes_filename(),
+ sb.toString()
+ );
+
+ ambiguous = true;
+
+ break;
+ }
+ }
+
+ return ambiguous ? null : FROM_NAME.get(class_name);
+ }
+ public static String abbreviation_from_name
+ (
+ final String name,
+ final boolean lazy
+ )
+ {
+ Classes c;
+
+ if (lazy)
+ {
+ c = null;
+
+ for (final String n: FROM_NAME.keySet())
+ {
+ if (name.startsWith(n))
+ {
+ c = ambiguous_entry_guard(name, n);
+
+ break;
+ }
+ }
+ }
+ else
+ {
+ c = FROM_NAME.get(name);
+ }
+
+ if (c == null)
+ {
+ return "unknown";
+ }
+
+ return c.abbreviation;
+ }
+
+ public static Set<String> get_all_abbreviations ()
+ {
+ return FROM_ABBREVIATION.keySet();
+ }
+
+ public static void read_all ()
+ throws Exception
+ {
+ BufferedReader br;
+ String input_line;
+ String[] data;
+ Classes new_class;
+
+ br = null;
+
+ try
+ {
+ br =
+ new BufferedReader
+ (
+ new InputStreamReader
+ (
+ new FileInputStream(Parameters.get_known_classes_filename())
+ )
+ );
+
+ while ((input_line = br.readLine()) != null)
+ {
+ data = input_line.split("::");
+
+ if (data.length != 3)
+ {
+ final StringBuilder sb;
+
+ sb = new StringBuilder();
+
+ sb.append("Invalid class entry: \"");
+ sb.append(input_line);
+
+ Error.ERROR.from_file
+ (
+ Parameters.get_known_classes_filename(),
+ sb.toString()
+ );
+
+ continue;
+ }
+
+ new_class = new Classes(data[1], data[2]);
+
+ if (!data[0].equals("") && FROM_ABBREVIATION.containsKey(data[2]))
+ {
+ final StringBuilder sb;
+
+ sb = new StringBuilder();
+
+ sb.append("Abbreviation \"");
+ sb.append(data[2]);
+ sb.append("\" is already in use.");
+
+ Error.ERROR.from_file
+ (
+ Parameters.get_known_classes_filename(),
+ sb.toString()
+ );
+
+ continue;
+ }
+
+ try
+ {
+ new_class.register();
+ }
+ catch (final Classes.AlreadyRegisteredException care)
+ {
+ final StringBuilder sb;
+
+ sb = new StringBuilder();
+
+ sb.append("Duplicate entry for class \"");
+ sb.append(data[1]);
+ sb.append("\".");
+
+ Error.ERROR.from_file
+ (
+ Parameters.get_known_classes_filename(),
+ sb.toString()
+ );
+
+ continue;
+ }
+ }
+ }
+ catch (final Exception e)
+ {
+ Error.FATAL.from_file
+ (
+ Parameters.get_known_classes_filename(),
+ "Error while reading class file:",
+ e.getMessage()
+ );
+
+ if (br != null)
+ {
+ try
+ {
+ br.close();
+ }
+ catch (final Exception e2)
+ {
+ Error.WARNING.from_file
+ (
+ Parameters.get_known_classes_filename(),
+ "Error while closing class file:",
+ e2.getMessage()
+ );
+ }
+ }
+
+ throw e;
+ }
+
+ try
+ {
+ br.close();
+ }
+ catch (final Exception e)
+ {
+ Error.WARNING.from_file
+ (
+ Parameters.get_known_classes_filename(),
+ "Error while closing class file:",
+ e.getMessage()
+ );
+
+ throw e;
+ }
+ }
+
+ private static class AlreadyRegisteredException extends Exception {}
+}
diff --git a/src/DHXCalParser.java b/src/DHXCalParser.java
new file mode 100644
index 0000000..bad0581
--- /dev/null
+++ b/src/DHXCalParser.java
@@ -0,0 +1,263 @@
+import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Calendar;
+import java.util.Locale;
+import java.util.Iterator;
+import java.util.TimeZone;
+
+import java.io.PrintWriter;
+
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.stream.XMLStreamException;
+
+/**
+ * This class stores the incoming data from XML.
+ * It is needed because of the stream nature of the XML parser.
+ **/
+public class DHXCalParser
+{
+ private final Group group;
+ private String uid_prefix;
+ private Event event;
+ private DataType data_type;
+ private int event_count;
+
+ public static void parse
+ (
+ final XMLStreamReader stream_reader,
+ final Group group,
+ final String uid_prefix
+ )
+ throws XMLStreamException
+ {
+ final DHXCalParser parser;
+
+ parser = new DHXCalParser(group, uid_prefix);
+
+ while (stream_reader.hasNext())
+ {
+ switch (stream_reader.next())
+ {
+ case XMLStreamReader.START_ELEMENT:
+ parser.handle_new_element(stream_reader);
+ break;
+
+ case XMLStreamReader.CDATA:
+ case XMLStreamReader.CHARACTERS:
+ parser.handle_data(stream_reader.getText().trim());
+ break;
+
+ case XMLStreamReader.END_DOCUMENT:
+ parser.flush();
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ private DHXCalParser
+ (
+ final Group group,
+ final String uid_prefix
+ )
+ {
+ this.group = group;
+ this.uid_prefix = uid_prefix;
+
+ event = null;
+ event_count = 0;
+ }
+
+ /**
+ * Called when entering an new element.
+ **/
+ private void handle_new_element (final XMLStreamReader stream_reader)
+ {
+ final DataType new_data_type;
+
+ new_data_type = DataType.get(stream_reader.getLocalName());
+
+ switch (new_data_type)
+ {
+ case EVENT:
+ /** We're done handling the previous event. **/
+ flush();
+ break;
+
+ default:
+ break;
+ }
+
+ data_type = new_data_type;
+ }
+
+ private void flush ()
+ {
+ if (event == null)
+ {
+ /**
+ * This is the first event, we don't have anything to write.
+ **/
+
+ event = new Event();
+ event.set_uid(uid_prefix + (event_count++));
+
+ return;
+ }
+
+ group.add_event(event);
+
+ event = new Event();
+ event.set_uid(uid_prefix + (event_count++));
+ }
+
+ /**
+ * Called when finding actual data.
+ **/
+ private void handle_data (final String data)
+ {
+ final String[] info;
+ final StringBuilder sb;
+ int i;
+
+ if (data.equals(""))
+ {
+ /**
+ * Data was garbage.
+ * It happens sometimes, I don't know why.
+ **/
+
+ /* [XXX][Improvement] Prevent this from happening. */
+ return;
+ }
+
+ /**
+ * Uses the previously set "data_type" to know where the data belongs.
+ **/
+ switch (data_type)
+ {
+ case START_DATE:
+ event.set_start_time(date_to_calendar(data));
+ break;
+
+ case END_DATE:
+ event.set_end_time(date_to_calendar(data));
+ break;
+
+ case TEXT:
+ info =
+ data.replaceAll
+ (
+ "<b>|</b>",
+ ""
+ ).replaceAll
+ (
+ "\\s+",
+ " "
+ ).split("<br />");
+
+ event.set_name(info[0]);
+
+ sb = new StringBuilder();
+
+ for (i = 1; i < info.length; ++i)
+ {
+ sb.append(info[i].trim());
+ sb.append(", ");
+ }
+
+ if (info.length > 1)
+ {
+ sb.delete((sb.length() - 2), sb.length());
+ }
+
+ event.set_description(sb.toString());
+ break;
+
+ case DETAILS:
+ // TODO
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ private static Calendar date_to_calendar (final String data)
+ {
+ final String[] day_hour;
+ final String[] hour;
+ final String[] day;
+ final java.util.Calendar result;
+
+ day_hour = data.split(" ");
+ hour = day_hour[1].split(":");
+ day = day_hour[0].split("-");
+
+ result =
+ java.util.Calendar.getInstance
+ (
+ TimeZone.getTimeZone("Europe/Paris"),
+ Locale.FRENCH
+ );
+
+ result.set(java.util.Calendar.DAY_OF_MONTH, Integer.parseInt(day[2]));
+
+ /* Java... don't ask... */
+ result.set(java.util.Calendar.MONTH, Integer.parseInt(day[1]) - 1);
+ result.set(java.util.Calendar.YEAR, Integer.parseInt(day[0]));
+
+ result.set(java.util.Calendar.HOUR_OF_DAY, Integer.parseInt(hour[0]));
+ result.set(java.util.Calendar.MINUTE, Integer.parseInt(hour[1]));
+
+ return result;
+ }
+
+ /**
+ * This enum is used to sort what is relevant in the XML file.
+ * Sadly, for justified reasons, Java does not permit access to the static
+ * members of an enum from its constructor, hence the "static"'s redundancy.
+ **/
+ private static enum DataType
+ {
+ IRRELEVANT,
+ EVENT,
+ START_DATE,
+ END_DATE,
+ TEXT,
+ DETAILS,
+ DATA;
+
+ private static final HashMap<String, DataType> FROM_STRING;
+
+ static
+ {
+ FROM_STRING = new HashMap<String, DataType>();
+
+ FROM_STRING.put("irrelevant", IRRELEVANT);
+ FROM_STRING.put("event", EVENT);
+ FROM_STRING.put("start_date", START_DATE);
+ FROM_STRING.put("end_date", END_DATE);
+ FROM_STRING.put("text", TEXT);
+ FROM_STRING.put("details", DETAILS);
+ FROM_STRING.put("data", DATA);
+ }
+
+ public static DataType get (final String e)
+ {
+ final DataType result;
+
+ result = FROM_STRING.get(e);
+
+ if (result == null)
+ {
+ return IRRELEVANT;
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/src/Error.java b/src/Error.java
new file mode 100644
index 0000000..2f40e4b
--- /dev/null
+++ b/src/Error.java
@@ -0,0 +1,196 @@
+import java.util.concurrent.Semaphore;
+
+import java.io.PrintWriter;
+import java.io.FileWriter;
+
+public enum Error
+{
+ WARNING("[W]"),
+ ERROR("[E]"),
+ FATAL("[F]"),
+ PROGRAM("[P]"); /* Errors that should not if the code's logic was correct. */
+
+ private static final Semaphore MUTEX;
+ private static PrintWriter OUTPUT;
+ private final String emblem;
+ private int count;
+
+ static
+ {
+ MUTEX = new Semaphore(1);
+ }
+
+ private Error (final String emblem)
+ {
+ this.emblem = emblem;
+ this.count = 0;
+ }
+
+ public static int init_handler ()
+ {
+ try
+ {
+ OUTPUT =
+ new PrintWriter
+ (
+ new FileWriter(Parameters.get_log_filename())
+ );
+ }
+ catch (final Exception e)
+ {
+ System.err.println("[F] Could not init the Error Handler:");
+ e.printStackTrace();
+
+ return -1;
+ }
+
+ return 0;
+ }
+
+ public static void finalize_handler ()
+ {
+ OUTPUT.flush();
+ OUTPUT.close();
+
+ summary();
+ }
+
+ public void from_thread
+ (
+ final String thread_id,
+ final String error
+ )
+ {
+ try
+ {
+ MUTEX.acquire();
+
+ OUTPUT.print(emblem);
+ OUTPUT.print("[Thread: ");
+ OUTPUT.print(thread_id);
+ OUTPUT.print("] ");
+ OUTPUT.println(error);
+
+ ++count;
+
+ MUTEX.release();
+ }
+ catch (final InterruptedException ie)
+ {
+ System.err.println
+ (
+ "[W] An thread was interrupted while trying to report an error."
+ );
+ }
+ }
+
+ public void from_thread
+ (
+ final String thread_id,
+ final String error,
+ final String report
+ )
+ {
+ try
+ {
+ MUTEX.acquire();
+
+ OUTPUT.print(emblem);
+ OUTPUT.print("[Thread: ");
+ OUTPUT.print(thread_id);
+ OUTPUT.print("] ");
+ OUTPUT.println(error);
+ OUTPUT.println(report);
+
+ ++count;
+
+ MUTEX.release();
+ }
+ catch (final InterruptedException ie)
+ {
+ System.err.println
+ (
+ "[W] An thread was interrupted while trying to report an error."
+ );
+ }
+ }
+
+ public void from_file
+ (
+ final String filename,
+ final String error
+ )
+ {
+ try
+ {
+ MUTEX.acquire();
+
+ OUTPUT.print(emblem);
+ OUTPUT.print("[File: ");
+ OUTPUT.print(filename);
+ OUTPUT.print("] ");
+ OUTPUT.println(error);
+
+ ++count;
+
+ MUTEX.release();
+ }
+ catch (final InterruptedException ie)
+ {
+ System.err.println
+ (
+ "[W] An thread was interrupted while trying to report an error."
+ );
+ }
+ }
+
+ public void from_file
+ (
+ final String filename,
+ final String error,
+ final String report
+ )
+ {
+ try
+ {
+ MUTEX.acquire();
+
+ OUTPUT.print(emblem);
+ OUTPUT.print("[File: ");
+ OUTPUT.print(filename);
+ OUTPUT.print("] ");
+ OUTPUT.println(error);
+ OUTPUT.println(report);
+
+ ++count;
+
+ MUTEX.release();
+ }
+ catch (final InterruptedException ie)
+ {
+ System.err.println
+ (
+ "[W] An thread was interrupted while trying to report an error."
+ );
+ }
+ }
+
+ private static void summary ()
+ {
+ final StringBuilder sb;
+
+ sb = new StringBuilder();
+
+ sb.append("Log report:\n");
+
+ for (final Error e: Error.values())
+ {
+ sb.append(e.emblem);
+ sb.append(" ");
+ sb.append(e.count);
+ sb.append("\n");
+ }
+
+ System.out.println(sb.toString());
+ }
+}
diff --git a/src/Event.java b/src/Event.java
new file mode 100644
index 0000000..4a56475
--- /dev/null
+++ b/src/Event.java
@@ -0,0 +1,286 @@
+import java.util.ArrayList;
+import java.util.List;
+
+import java.util.Locale;
+import java.util.Calendar;
+import java.util.TimeZone;
+
+public class Event
+{
+ private final List<String> locations;
+ private final List<String> speakers;
+ private final List<String> attendees;
+ private String name;
+ private String category;
+ private String uid;
+ private String description;
+ private java.util.Calendar start_time;
+ private java.util.Calendar end_time;
+ private java.util.Calendar creation_time;
+
+ public Event ()
+ {
+ locations = new ArrayList<String>();
+ speakers = new ArrayList<String>();
+ attendees = new ArrayList<String>();
+
+ name = null;
+ category = null;
+ description = null;
+ start_time = null;
+ end_time = null;
+
+ creation_time =
+ java.util.Calendar.getInstance
+ (
+ TimeZone.getTimeZone("Europe/Paris"),
+ Locale.FRENCH
+ );
+ }
+
+ public String get_name ()
+ {
+ return name;
+ }
+
+ public void set_name (final String val)
+ {
+ if (val != null)
+ {
+ name =
+ val.trim
+ (
+ ).replaceAll
+ (
+ "(?<!\\\\),",
+ "\\\\,"
+ ).replaceAll
+ (
+ "(?<!\\\\);",
+ "\\\\;"
+ );
+ }
+ }
+
+ public void set_description (final String val)
+ {
+ if (val != null)
+ {
+ description =
+ val.replaceAll
+ (
+ "(?<!\\\\),",
+ "\\\\,"
+ ).replaceAll
+ (
+ "(?<!\\\\);",
+ "\\\\;"
+ );
+ }
+ }
+
+ public void set_category (final String val)
+ {
+ if (val != null)
+ {
+ category = val;
+ }
+ }
+
+ public void set_uid (final String val)
+ {
+ if (val != null)
+ {
+ uid = val;
+ }
+ }
+
+ public void set_start_time (final java.util.Calendar val)
+ {
+ if (val != null)
+ {
+ start_time = val;
+ }
+ }
+
+ public void set_end_time (final java.util.Calendar val)
+ {
+ if (val != null)
+ {
+ end_time = val;
+ }
+ }
+
+ public void add_location (final String str)
+ {
+ locations.add(str);
+ }
+
+ public void add_speaker (final String str)
+ {
+ speakers.add(str);
+ }
+
+ public void add_attendee (final String str)
+ {
+ attendees.add(str);
+ }
+
+ public String toString()
+ {
+ final StringBuilder sb;
+ final String creation_time_str;
+
+ sb = new StringBuilder();
+
+ creation_time_str = get_time(creation_time);
+
+ sb.append("BEGIN:VEVENT");
+
+ sb.append("\r\nSUMMARY:");
+
+ if (category != null)
+ {
+ sb.append("[");
+ sb.append(category);
+ sb.append("] ");
+ }
+
+ if (name != null)
+ {
+ sb.append(name);
+ }
+
+ if (!locations.isEmpty())
+ {
+ sb.append("\r\nLOCATION:");
+
+ for (final String s: locations)
+ {
+ sb.append(s);
+ sb.append("\\n");
+ }
+
+ sb.delete(sb.length() - 2, sb.length());
+ }
+
+ sb.append("\r\nDESCRIPTION:");
+
+ if (description != null)
+ {
+ sb.append(description);
+
+ if
+ (
+ !speakers.isEmpty()
+ || !attendees.isEmpty()
+ )
+ {
+ sb.append("\\n");
+ }
+ }
+ if (!speakers.isEmpty())
+ {
+
+ for (final String s: speakers)
+ {
+ sb.append("[Speaker] ");
+ sb.append(s);
+ sb.append("\\n");
+ }
+
+ if (attendees.isEmpty())
+ {
+ sb.delete(sb.length() - 2, sb.length());
+ }
+ }
+
+ if (!attendees.isEmpty())
+ {
+
+ for (final String s: attendees)
+ {
+ sb.append("[Attendee] ");
+ sb.append(s);
+ sb.append("\\n");
+ }
+
+ sb.delete(sb.length() - 2, sb.length());
+ }
+
+
+ sb.append("\r\nUID:");
+ sb.append(uid);
+
+ sb.append("\r\nCREATED:");
+ sb.append(creation_time_str);
+
+ sb.append("\r\nLAST-MODIFIED:");
+ sb.append(creation_time_str);
+
+ sb.append("\r\nDTSTART:");
+ sb.append(get_time(start_time));
+
+ sb.append("\r\nDTEND:");
+ sb.append(get_time(end_time));
+
+ sb.append("\r\nSTATUS:CONFIRMED");
+
+ sb.append("\r\nSEQUENCE:0");
+
+ sb.append("\r\nTRANSP:OPAQUE");
+
+ sb.append("\r\nEND:VEVENT");
+
+ return sb.toString();
+ }
+
+ private String get_time (final java.util.Calendar cal)
+ {
+ final StringBuilder sb;
+ int val;
+
+ sb = new StringBuilder();
+
+ sb.append(cal.get(java.util.Calendar.YEAR));
+
+ val = cal.get(java.util.Calendar.MONTH) + 1; /* Java... don't ask... */
+
+ if (val < 10)
+ {
+ sb.append("0");
+ }
+
+ sb.append(val);
+
+ append_two_digit(sb, cal, java.util.Calendar.DAY_OF_MONTH);
+
+ sb.append("T");
+
+ append_two_digit(sb, cal, java.util.Calendar.HOUR_OF_DAY);
+ append_two_digit(sb, cal, java.util.Calendar.MINUTE);
+
+ sb.append("00");
+
+ return sb.toString();
+ }
+
+ private void append_two_digit
+ (
+ final StringBuilder sb,
+ final java.util.Calendar cal,
+ final int val_id
+ )
+ {
+ final int val;
+
+ val = cal.get(val_id);
+
+ if (val < 10)
+ {
+ sb.append("0");
+ }
+
+ sb.append(val);
+ }
+}
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 {}
+}
diff --git a/src/ICSParser.java b/src/ICSParser.java
new file mode 100644
index 0000000..ac4f96d
--- /dev/null
+++ b/src/ICSParser.java
@@ -0,0 +1,99 @@
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+
+import java.security.MessageDigest;
+
+import java.net.URL;
+
+public class ICSParser
+{
+
+ public static void parse
+ (
+ final String url,
+ final Group group,
+ final boolean lazy,
+ final boolean uid_override
+ )
+ throws Exception
+ {
+ final BufferedReader reader;
+ final StringBuilder sb;
+
+ boolean should_record, has_id, ignore_line;
+ String line;
+ String class_name;
+ String hash_seed;
+ int length;
+ sb = new StringBuilder();
+ reader =
+ new BufferedReader(new InputStreamReader((new URL(url)).openStream()));
+
+ class_name = "unknown";
+ should_record = false;
+ ignore_line = false;
+
+ hash_seed = "";
+ has_id = false;
+
+ while ((line = reader.readLine()) != null)
+ {
+ if (line.startsWith("END:VEVENT"))
+ {
+ if (!has_id)
+ {
+ sb.append("UID:noid_");
+ sb.append(hash_seed.hashCode());
+ sb.append("\r\n");
+ }
+
+ sb.append(line);
+ sb.append("\r\n");
+
+ should_record = false;
+
+ group.add_ICS_fragment(sb.toString(), class_name, lazy);
+ sb.setLength(0);
+ }
+ else if (line.startsWith("BEGIN:VEVENT"))
+ {
+ should_record = true;
+ has_id = false;
+ hash_seed = "";
+ }
+ else if (line.startsWith("DTSTART:"))
+ {
+ hash_seed += line;
+ }
+ else if (line.startsWith("SUMMARY:"))
+ {
+ class_name = line.substring(8, line.length());
+ hash_seed += line;
+ }
+ else if (line.startsWith("UID"))
+ {
+ if (uid_override)
+ {
+ ignore_line = true;
+ }
+ else
+ {
+ has_id = true;
+ }
+ }
+
+ if (line.matches("\\w+:.*") && should_record)
+ {
+ if (!ignore_line)
+ {
+ sb.append(line);
+ sb.append("\r\n");
+ }
+
+ ignore_line = false;
+ }
+ }
+
+ reader.close();
+ }
+}
diff --git a/src/Parameters.java b/src/Parameters.java
new file mode 100644
index 0000000..d323908
--- /dev/null
+++ b/src/Parameters.java
@@ -0,0 +1,135 @@
+public class Parameters
+{
+ private static String output_dir;
+ private static String log_filename, known_classes_filename, groups_filename;
+ private static int threads;
+
+ static
+ {
+ output_dir = "../output/";
+ log_filename = "../last_log";
+ known_classes_filename = "../data/KNOWN_CLASSES";
+ groups_filename = "../data/GROUPS";
+ threads = 8;
+ }
+
+ private Parameters () {} /** utility class **/
+
+ public static boolean set_parameters (final String[] args)
+ {
+ int i;
+
+ for (i = 0; i < args.length; i++)
+ {
+ if(args[i].equals("--outputs"))
+ {
+ i++;
+ output_dir = args[i];
+ }
+ else if(args[i].equals("--log"))
+ {
+ i++;
+ log_filename = args[i];
+ }
+ else if(args[i].equals("--classes"))
+ {
+ i++;
+ known_classes_filename = args[i];
+ }
+ else if(args[i].equals("--groups"))
+ {
+ i++;
+ groups_filename = args[i];
+ }
+ else if(args[i].equals("--threads"))
+ {
+ i++;
+ threads = Integer.parseInt(args[i]);
+ }
+ else if(args[i].equals("--help") || args[i].equals("-h"))
+ {
+ usage();
+
+ return false;
+ }
+ else
+ {
+ System.err.println("[W] Unknown parameter:" + args[i]);
+ }
+ }
+
+ return true;
+ }
+
+ private static void usage ()
+ {
+ final StringBuilder sb;
+
+ sb = new StringBuilder();
+
+ sb.append
+ (
+ ").\n\"--outputs directory\": where the 'ICS' files are written "
+ + "(default: "
+ );
+ sb.append(output_dir);
+
+ sb.append
+ (
+ ").\n\"--log filename\": where the errors are reported (default: "
+ );
+ sb.append(log_filename);
+
+ sb.append
+ (
+ ").\n\"--classes filename\": file containing the known classes "
+ + "(default: "
+ );
+ sb.append(known_classes_filename);
+
+ sb.append
+ (
+ ").\n\"--groups filename\": file containing the groups (default: "
+ );
+ sb.append(groups_filename);
+
+ sb.append
+ (
+ ").\n\"--threads number\": maximum number of threads running at "
+ + "any time (default: "
+ );
+ sb.append(threads);
+
+ sb.append
+ (
+ ").\n\"--help\": prints this instead of converting (alias: \"-h\")."
+ );
+
+ System.out.println(sb.toString());
+ }
+
+ public static String get_output_directory ()
+ {
+ return output_dir;
+ }
+
+ public static String get_log_filename ()
+ {
+ return log_filename;
+ }
+
+ public static String get_known_classes_filename ()
+ {
+ return known_classes_filename;
+ }
+
+ public static String get_groups_filename ()
+ {
+ return groups_filename;
+ }
+
+ public static int get_max_threads ()
+ {
+ return threads;
+ }
+}