utilfichiers
L'objectif de cette partie est de lire les lignes à analyser dans une base relationnelle.
Dans votre compte oracle, accessible via http://b710ntb.univ-lyon1.fr:5560/isqlplus (ou depuis l'extérieur de l'université via la méthode décrite ici), créer une table donnees
à l'aide du script suivant:
DROP TABLE donnees; CREATE TABLE donnees ( SOURCE VARCHAR(255) NOT NULL, num INTEGER NOT NULL, ligne VARCHAR(255), PRIMARY KEY (SOURCE,num) ); -- -- Contenu de la table donnees -- INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/Arguments.java', 0, '/*'); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/Arguments.java', 1, ' * To change this template, choose Tools | Templates'); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/Arguments.java', 2, ' * and open the template in the editor.'); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/Arguments.java', 3, ' */'); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/Arguments.java', 4, ''); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/Arguments.java', 5, 'package utilfichiers;'); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/Arguments.java', 6, ''); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/Arguments.java', 7, '/**'); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/Arguments.java', 8, ' *'); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/Arguments.java', 9, ' * @author emmanuelcoquery'); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/Arguments.java', 10, ' */'); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/Arguments.java', 11, 'public class Arguments {'); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/Arguments.java', 12, ''); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/Arguments.java', 13, ' public static void main(String [] args) {'); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/Arguments.java', 14, ' for(int i = 0; i< args.length; i++) {'); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/Arguments.java', 15, ' System.out.println(args[i]);'); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/Arguments.java', 16, ' }'); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/Arguments.java', 17, ' }'); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/Arguments.java', 18, ''); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/Arguments.java', 19, '}'); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/LecteurdeFichier.java', 0, '/*'); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/LecteurdeFichier.java', 1, ' * To change this template, choose Tools | Templates'); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/LecteurdeFichier.java', 2, ' * and open the template in the editor.'); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/LecteurdeFichier.java', 3, ' */'); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/LecteurdeFichier.java', 4, ''); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/LecteurdeFichier.java', 5, 'package utilfichiers;'); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/LecteurdeFichier.java', 6, 'import java.io.*;'); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/LecteurdeFichier.java', 7, '/**'); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/LecteurdeFichier.java', 8, ' *'); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/LecteurdeFichier.java', 9, ' * @author emmanuelcoquery'); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/LecteurdeFichier.java', 10, ' */'); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/LecteurdeFichier.java', 11, 'public class LecteurDeFichier {'); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/LecteurdeFichier.java', 12, ''); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/LecteurdeFichier.java', 13, ' private BufferedReader reader;'); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/LecteurdeFichier.java', 14, ''); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/LecteurdeFichier.java', 15, ' public LecteurDeFichier(String filename) throws FileNotFoundException {'); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/LecteurdeFichier.java', 16, ' reader = new BufferedReader(new FileReader(filename));'); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/LecteurdeFichier.java', 17, ' }'); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/LecteurdeFichier.java', 18, ''); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/LecteurdeFichier.java', 19, ' public String lireLigne() throws IOException {'); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/LecteurdeFichier.java', 20, ' return reader.readLine();'); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/LecteurdeFichier.java', 21, ' }'); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/LecteurdeFichier.java', 22, ''); INSERT INTO donnees (SOURCE, num, ligne) VALUES('src/utilfichiers/LecteurdeFichier.java', 23, '}'); COMMIT;
On souhaite dans un premier temps créer une classe permettant d'ouvrir une connexion Oracle. Afin d'utiliser l'aspect général de JDBC, on va tout d'abord créer une interface ConnectionProvider
comme suit:
package utilfichiers; import java.sql.Connection; import java.sql.SQLException; public interface ConnectionProvider { public Connection getConnection() throws SQLException; }
Télécharger le fichier ojdbc14.jar depuis le site http://www.oracle.com. Une copie de la documentation JavaDoc se trouve ici. Ajouter ce fichier à votre projet de la manière suivante: Clic droit sur le “répertoire” Libraries de votre projet → Add JAR/Folder. Indiquer alors l'emplacement où vous avez sauvé le fichier ojdbc14.jar. Le pilote JDBC pour Oracle est maintenant accessible au projet.
Créer une classe OracleProvider
1) qui implémente ConnectionProvider
. Cette classe comportera un champ ods
de la classe oracle.jdbc.pool.OracleDataSource
qui sera initialisé dans le constructeur avec un code similaire au code suivant:
ods = new OracleDataSource(); ods.setDriverType("thin"); ods.setServerName("pedagowin710"); ods.setPortNumber(1521); ods.setDatabaseName("ora10g"); ods.setUser("votreLoginOracle"); ods.setPassword("votreMotDePasseOracle");
Remarque: changer pedagowin710
en localhost
depuis l'extérieur de l'université en ssh
Enfin ajouter la méthode getConnection()
qui renvoie le résultat de l'appel à getConnection()
sur le champ ods
.
Ajouter à la classe OracleProvider
une méthode main
pour la tester. Dans cette méthode, créer un objet OracleProvider
, et l'utiliser pour récupérer un objet du type java.sql.Connection
. Utiliser cet objet pour créer un objet de type java.sql.Statement
via la méthode createStatement()
. Cet objet va être utilisé pour interroger la base de donnée. Utiliser la méthode executeQuery(String sql)
du Statement
pour exécuter la requête suivante:
SELECT ligne FROM donnees
On stockera le résultat dans un objet de type java.sql.ResultSet
. On utilisera ensuite la méthode next()
et la méthode getString(String column)
de cet objet pour parcourir le résultat dans une boucle while
et l'afficher (via System.out.println(String s)
). Lancez ensuite le code via NetBeans.
On pourra s'inspirer des extraits de code suivants:
// Création du statement Statement stat = .... // Exécution de la requête ResultSet rs = stat.executeQuery("SELECT ......"); // Parcours du résultat while (rs.next()) { ...... rs.getString("ligne") ...... }
Créer une classe LecteurBD
qui implémente l'interface Lecteur
. Elle possédera des champs connection
de la classe java.sql.Connection
et result
de la classe java.sql.ResultSet
.
Le constructeur prendra en argument un ConnectionProvider
, ainsi qu'une String
qui contiendra une requête SQL. On supposera que la première colonne du résultat de cette requête se nomme ligne
et contient des chaînes de caractères.
Le constructeur utilisera le ConnectionProvider
pour récupérer une Connection
et créera un Statement
via cette dernière. On utilisera ce Statement
pour exécuter la requête passée en argument, dont le résultat sera placé dans le champ result
.
Il reste enfin à ajouter la méthode lireLigne()
. Elle fonctionnera de la manière suivante:
next()
du champ result
.null
, après avoir fermé la Connection
ligne
Il reste un problème: la méthode lireLigne
peut renvoyer une IOException
et pas une SQLException
. Pour contourner le problème, on ajoutera un try
/catch
autour de coeur de la méthode, avec le code suivant pour la partie catch
:
} catch (SQLException e) { throw new IOException(e); }
Enfin modifier la méthode main de l'analyseur pour que si le nom du fichier commence par oracle:
, l'analyseur utilise un LecteurBD initialisé avec une OracleProvider et avec la requête suivante:
SELECT ligne FROM donnees
Exercice supplémentaire: modifier à nouveau cette méthode pour que la requête corresponde à la fin de l'argument. Par exemple l'argument oracle:SELECT\ ligne\ FROM\ donnees
correspondrait à l'utilisation de la requête précédente.
L'objectif ici est de permettre d'analyser un ensemble de fichiers en une fois en spécifiant plusieurs fichiers sur la ligne de commande. Lorsqu'un fichier est un répertoire, on analysera alors les fichiers qui sont dans le répertoire, ainsi que tous les sous-répertoires.
Pour cela on procédera en deux étapes: la première étape consistera à
construire un ensemble (i.e. une Collection
) de
Lecteur
s, un pour chaque fichier à analyser.
La deuxième étape consistera à parcourir cette collection pour lancer
l'analyse sur chacun des Lecteur
s.
Le rôle de cette classe sera de construire la Collection
mentionnée ci-dessus.
Elle devra comporter un champ lecteurs
de type
Collection<Lecteur>
. Ce champ sera initialisé dans le
constructeur de la classe en utilisant une implémentation de
Collection
que vous choisirez.
Elle comportera également une méthode public
Collection<Lecteur> getLecteurs()
qui renverra le champ
lecteurs
.
Enfin on ajoutera une méthode public void ajoute(String
fichier)
qui se comportera de la manière suivante:
fichier
est égal à “-”
, on ajoute un LecteurClavier
à lecteurs
.fichier
est une URL (si vous avez déjà fait la première amélioration), on ajoute un LecteurReseau
pour cette URL dans lecteurs
.fichier
désigne soit un fichier, soit un répertoire. Dans ce cas on créera une instance de la classe java.io.File
à partir de fichier
. On utilisera cet objet pour distinguer les cas suivants:isFile()
), ajouter un nouveau LecteurDeFichier
pour ce fichier.isDirectory()
), récupérer l'ensemble des fichiers et des répertoires contenus dans celui-ci en utilisant la méthode listFiles()
, qui renvoie un tableau de File
. Pour chaque File
dans ce tableau, on appellera récursivement la méthode ajoute
et utilisant au passage la méthode getCanonicalPath()
pour transformer ce File
en String
.exists()
) lever une nouvelle exception java.io.FileNotFoundException
. On passera le nom du fichier en argument au constructeur de l'exception.IOException
On passera le nom du fichier en argument au constructeur de l'exception.
Au début de la méthode main
de la classe Analyseur
,
créer une instance de GestionArguments
. Créer ensuite
l'analyseur en utilisant le premier argument de la ligne de commande.
ajoute
de votre GestionArguments
avec comme paramètre la chaîne “-”
pour ajouter un LecteurClavier
.ajoute
.
Remarque: on peut gérer les exceptions
FileNotFoundException
et IOException
lancées dans
ajoute
à cet endroit en affichant un message d'erreur, ce
qui permet de traiter les autres fichiers.
Une fois tous les arguments traités, on récupère les lecteurs via la
méthode getLecteurs
, puis on parcours la collection ainsi
obtenue, par exemple avec la nouvelle forme de la boucle for
.
Pour chacun des Lecteur
s de la collection, on appelle la
méthode analyse
de l'Analyseur
et on affiche le
résultat comme au TP 1 ou au TP 2.
Le but est d'améliorer les informations affichées lorsqu'une ligne est
trouvée par l'analyseur. Pour cela, on ajoute dans la classe Ligne des
informations sur l'endroit d'où provient la ligne. Cela aura un impact
sur l'interface Lecteur
. On en profitera pour améliorer les
implémentations de cette interface à l'aide d'une classe abstraite.
Remarque: cette amélioration se combine très bien avec la précédente pour obtenir des résultats plus clairs.
Afin de savoir d'où provient la ligne représentée par l'objet, on
ajoute un champ String provenance
à la classe Ligne
.
Modifier le constructeur pour prendre un argument supplémentaire indiquant cette provenance.
Ajouter une méthode String getProvenance()
qui renvoie le
champ provenance
.
Enfin ajouter une méthode public String toString()
qui
renverra une chaîne de caractères ppp:nnn:ttt où
ppp est la provenance, nnn est le numéro de ligne et
enfin ttt
est le texte de la ligne.
On change l'interface Lecteur
de la manière suivante: la
méthode
public String lireLigne() throws IOException;
devient
public Ligne lireLigne() throws IOexception;
En effet, c'est le Lecteur
qui disposera des informations sur
la provenance des lignes. Une conséquence de cette modification est
que ce sont à présent les Lecteur
s qui vont se charger de la
numérotation des lignes.
On peut remarquer que les implémentations de l'interface
Lecteur
se ressemblent: mise à part le LecteurBD, elles utilisent toutes un
BufferedReader
pour lire les lignes. Comme on doit ajouter à
présent un compteur de lignes, on peut ce dire que cela va encore
faire du code à dupliquer dans les différentes implémentations.
Créer une classe abstraite LecteurAbstrait
et déclarer
qu'elle implémente l'interface Lecteur
.
Ajouter un champ BufferedReader source
. On ne s'occupera pas
de l'initialisation de ce champ dans le constructeur de la classe.
À la place, on va créer une méthode qui va s'occuper de
cela. L'utilité de cette manière de procéder apparaîtra au moment où
on étendra cette classe.
Ajouter une méthode protected void setReader(Reader reader)
qui initialise le champ source
en passant reader
en argument
du constructeur de la classe BufferedReader
.
Ajouter un champ int numLigne
. Dans le constructeur,
initialiser ce champ à 0
.
Ajouter un champ String origine
et modifier le constructeur
pour prendre un paramètre qui va initialiser ce champ.
On a à présent toutes les briques pour écrire la méthode
lireLigne()
de l'interface Lecteur
. Plus
précisement, cette méthode va exécuter les actions suivantes:
readLine()
du champ source
;Ligne
en utilisant le texte, le numéro de ligne et le champ origine
pour la provenance;Ligne
.
On peut à présent recoder de manière simple les différentes
implémentation de Lecteur
(sauf le LecteurBD
): pour chacune d'elles, il suffit de
procéder comme suit:
LecteurAbstrait
;super
avec comme argument la chaîne de caractères qui sert à spécifier l'origine des lignes;BufferedReader
: en effet il y a déjà un BufferedReader
dans LecteurAbstrait
.BufferedReader
en un appel à la méthode setReader
(qui est héritée de LecteurAbstrait
).lireLigne()
: elle est déjà implémentée dans LecteurAbstrait
.
On peut voir qu'au final, chacune des implémentations de
Lecteur
est plus courte qu'avant.
BufferedReader
une fois que toutes les lignes ont été lues. On peut également créer les Reader
s au moment où la première ligne est demandée. Pour cela on peut utiliser un champ booléen ainsi qu'une méthode abstraite protected void initReader()
qui remplace la méthode setReader(Reader)
. Cette méthode sera appelée dans lireLigne()
la première fois.Lecteur
à partir d'une collection de lignes. Utiliser ensuite cette implémentation pour enchaîner les analyses et permettre de chercher les lignes contenant un ensemble de mots.utilfichiers