1 /*
2 * PCUServeur for the PCU project.
3 * Copyright (C) 2010 PLU Julien
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * For questions: julien.plu@redaction-developpez.com
19 */
20 package pcuserveur.lib;
21
22 import java.io.IOException;
23 import java.util.HashMap;
24 import java.util.Map.Entry;
25
26 /**
27 * @author PLU Julien
28 * @version 1.0
29 *
30 * <b>
31 * Un canal est un groupe de discussion plusieurs-à-plusieurs.
32 * Identifié par un nom, unique et sans espaces, il possède également
33 * un sujet qui le décrit brièvement, et la liste des gestionnaires de
34 * connexion formant le groupe. Sa signature est le triplet (
35 * <code>nom</code>, "<code>sujet</code>", <code>nombreMembres</code>).
36 * </b>
37 * <p>
38 * Un Canal est caractérisé par les informations suivantes:
39 * <ul>
40 * <li>Une HashMap ayant un String pour clef et un
41 * GestionnaireConnexion pour valeur.</li>
42 * <li>Un String pour le nom du canal.</li>
43 * <li>Un String pour le sujet du canal.</li>
44 * </ul>
45 * </p>
46 *
47 * @see GestionnaireConnexion
48 * @see Evenement
49 * @see Serveur
50 * @see Vide
51 * @see DejaConnecteException
52 * @see NonConnecteException
53 */
54 public class Canal implements Vide {
55 /**
56 * <p>
57 * Liste des connexions contenues dans le canal.
58 * </p>
59 *
60 * @see Canal#getSignature ()
61 * @see Canal#ajouterGC (GestionnaireConnexion)
62 * @see Canal#enleverGC (GestionnaireConnexion)
63 * @see Canal#enleverGC (String)
64 * @see Canal#listerMembre ()
65 * @see Canal#notifier (Evenement, String, String)
66 * @see Canal#vider ()
67 */
68 private final HashMap<String, GestionnaireConnexion> listeGestionnaires = new HashMap<String, GestionnaireConnexion> ();
69 /**
70 * <p>
71 * Nom du canal.
72 * </p>
73 *
74 * @see Canal#getNom ()
75 * @see Canal#Canal (String, String)
76 * @see Canal#estVide ()
77 * @see Canal#initialiser (String, String)
78 * @see Canal#notifier (Evenement, String, String)
79 * @see Canal#getSignature ()
80 * @see Canal#vider ()
81 */
82 private String nom;
83 /**
84 * <p>
85 * Sujet du canal.
86 * </p>
87 *
88 * @see Canal#getSujet ()
89 * @see Canal#Canal (String, String)
90 * @see Canal#changerSujet (String)
91 * @see Canal#estVide ()
92 * @see Canal#initialiser (String, String)
93 * @see Canal#getSignature ()
94 * @see Canal#vider ()
95 */
96 private String sujet;
97
98 /**
99 * <p>
100 * Constructeur par défaut.
101 * </p>
102 */
103 public Canal () {
104 // Constructeur par défaut
105 }
106
107 /**
108 * <p>
109 * Constructeur paramétré.
110 * </p>
111 *
112 * @param nom1
113 * Le nom.
114 * @param sujet1
115 * Le sujet.
116 */
117 public Canal (final String nom1, final String sujet1) {
118 this.nom = nom1;
119 this.sujet = sujet1;
120 }
121
122 /**
123 * <p>
124 * Ajoute un gestionnaire de connexion au canal. Après avoir ajouté
125 * <code>gc</code>, les autres membres du canal sont notifiés de son
126 * arrivée.
127 * </p>
128 *
129 * @param gc
130 * Le gestionnaire de connexion à ajouter.
131 *
132 * @throws DejaConnecteException
133 * <code>gc<code> est déjà dans le canal.
134 * @throws IOException
135 * Un envoie lors de la notification a échoué.
136 */
137 public void ajouterGC (final GestionnaireConnexion gc) throws DejaConnecteException, IOException {
138 final String login = gc.getClient ().getLogin ();
139
140 if (!this.listeGestionnaires.containsKey (login)) {
141 this.listeGestionnaires.put (login, gc);
142 this.notifier (Evenement.REJOINT, login, ""); //$NON-NLS-1$
143 }
144 else {
145 throw new DejaConnecteException (login + " est deja dans " + this.nom); //$NON-NLS-1$
146 }
147 }
148
149 /**
150 * <p>
151 * Change le sujet du canal. Après avoir changé le sujet, les autres membres
152 * du canal sont notifiés du changement.
153 * </p>
154 *
155 * @param sujet1
156 * Le nouveau sujet.
157 *
158 * @throws IOException
159 * Un envoie lors de la notification a échoué.
160 */
161 public void changerSujet (final String sujet1) throws IOException {
162 this.sujet = sujet1;
163
164 this.notifier (Evenement.SUJETCHANGE, "", sujet1); //$NON-NLS-1$
165 }
166
167 /**
168 * <p>
169 * Enlève un gestionnaire de connexion du canal. Après avoir enlevé
170 * <code>gc</code>, les autres membres du canal sont notifiés de son départ.
171 * </p>
172 *
173 * @param gc
174 * Le gestionnaire de connexion à retirer.
175 *
176 * @throws NonConnecteException
177 * <code>gc</code> n'est pas dans le canal.
178 * @throws IOException
179 * Un envoie lors de la notification a échoué.
180 */
181 public synchronized void enleverGC (final GestionnaireConnexion gc) throws NonConnecteException, IOException {
182 final String login = gc.getClient ().getLogin ();
183
184 if (this.listeGestionnaires.containsKey (login)) {
185 this.listeGestionnaires.remove (login);
186 this.notifier (Evenement.PARTI, login, ""); //$NON-NLS-1$
187 }
188 else {
189 throw new NonConnecteException (login + " n'est pas dans " + this.nom); //$NON-NLS-1$
190 }
191 }
192
193 /**
194 * <p>
195 * Enlève un gestionnaire de connexion du canal. Après avoir enlevé
196 * <code>login</code>, les autres membres du canal sont notifiés de son
197 * départ.
198 * </p>
199 *
200 * @param login
201 * Login du client à enlever.
202 *
203 * @throws NonConnecteException
204 * <code>login</code> n'est pas dans le canal.
205 */
206 public synchronized void enleverGC (final String login) throws NonConnecteException {
207 if (this.listeGestionnaires.containsKey (login)) {
208 this.listeGestionnaires.remove (login);
209 }
210 else {
211 throw new NonConnecteException (login + " n'est pas dans " + this.nom); //$NON-NLS-1$
212 }
213 }
214
215 /**
216 * <p>
217 * Savoir si le canal est vide.
218 * </p>
219 *
220 * @return <code>true</code> si le canal est vide, <code>false</code> sinon.
221 */
222 public boolean estVide () {
223 return (this.nom == null) && (this.sujet == null);
224 }
225
226 /**
227 * <p>
228 * Obtenir le nom du canal, si il existe.
229 * </p>
230 *
231 * @return Le nom, ou <code>null</code> si le canal est vide.
232 */
233 public String getNom () {
234 return this.nom;
235 }
236
237 /**
238 * <p>
239 * Obtenir la signature du canal.
240 * </p>
241 *
242 * @return La signature du canal, ou <code>null</code> si il est vide.
243 */
244 public String getSignature () {
245 return this.estVide () ? null : this.nom + " \"" + this.sujet + "\" " + this.listeGestionnaires.size (); //$NON-NLS-1$ //$NON-NLS-2$
246 }
247
248 /**
249 * <p>
250 * Obtenir le sujet du canal, si il existe.
251 * </p>
252 *
253 * @return Le sujet, ou <code>null</code> si le canal est vide.
254 */
255 public String getSujet () {
256 return this.sujet;
257 }
258
259 /**
260 * <p>
261 * Initialise le canal, à la façon du constructeur paramétré.
262 * </p>
263 *
264 * @param nom1
265 * Le nom.
266 * @param sujet1
267 * Le sujet.
268 */
269 public void initialiser (final String nom1, final String sujet1) {
270 this.nom = nom1;
271 this.sujet = sujet1;
272 }
273
274 /**
275 * <p>
276 * Initialise le canal, à la façon du constructeur paramétré.
277 * </p>
278 *
279 * @return la liste des membres du canal
280 */
281 public String listerMembre () {
282 final StringBuffer res = new StringBuffer ();
283 int i = 0;
284
285 for (final Entry<String, GestionnaireConnexion> entry:this.listeGestionnaires.entrySet ()) {
286 final GestionnaireConnexion valeur = entry.getValue ();
287
288 res.append (valeur.getClient ().getLogin ());
289 if (i != this.listeGestionnaires.size () - 1) {
290 res.append (" "); //$NON-NLS-1$
291 }
292 i++;
293 }
294
295 return res.toString ();
296 }
297
298 /**
299 * <p>
300 * Notifie tous les membres du canal, d'un évènement l'affectant.
301 * </p>
302 *
303 * @param evenement
304 * L'événement à notifier.
305 * @param login
306 * Le login. Si il n'est pas pertinant, la chaîne est vide.
307 * @param texte
308 * Le texte. Si il n'est pas pertinant, la chaîne est vide.
309 *
310 * @throws IOException
311 * Un envoie lors de la notification a échoué.
312 */
313 public synchronized void notifier (final Evenement evenement, final String login, final String texte) throws IOException {
314 for (final Entry<String, GestionnaireConnexion> entry:this.listeGestionnaires.entrySet ()) {
315 final GestionnaireConnexion valeur = entry.getValue ();
316
317 if (valeur != null) {
318 valeur.notifier (this.nom, evenement, login, texte);
319 }
320 }
321 }
322
323 /**
324 * <p>
325 * Vide le canal. Positionne le nom et le sujet à <code>null</code>, et
326 * supprime tous les éléments de la liste des gestionnaires de connexion. A
327 * utiliser avec beaucoup de précaution.
328 * </p>
329 *
330 * @see Vide
331 */
332 public void vider () {
333 this.nom = null;
334 this.sujet = null;
335 this.listeGestionnaires.clear ();
336 }
337 }