Set URIs and latest edition differently.
[Sone.git] / src / main / java / net / pterodactylus / sone / core / Core.java
1 /*
2  * Sone - Core.java - Copyright © 2010 David Roden
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 package net.pterodactylus.sone.core;
19
20 import java.net.MalformedURLException;
21 import java.util.ArrayList;
22 import java.util.Collections;
23 import java.util.HashMap;
24 import java.util.HashSet;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Set;
28 import java.util.logging.Level;
29 import java.util.logging.Logger;
30
31 import net.pterodactylus.sone.core.Options.DefaultOption;
32 import net.pterodactylus.sone.core.Options.Option;
33 import net.pterodactylus.sone.core.Options.OptionWatcher;
34 import net.pterodactylus.sone.data.Post;
35 import net.pterodactylus.sone.data.Reply;
36 import net.pterodactylus.sone.data.Sone;
37 import net.pterodactylus.sone.freenet.wot.Identity;
38 import net.pterodactylus.sone.freenet.wot.IdentityListener;
39 import net.pterodactylus.sone.freenet.wot.IdentityManager;
40 import net.pterodactylus.sone.freenet.wot.OwnIdentity;
41 import net.pterodactylus.util.config.Configuration;
42 import net.pterodactylus.util.config.ConfigurationException;
43 import net.pterodactylus.util.logging.Logging;
44 import net.pterodactylus.util.number.Numbers;
45 import freenet.keys.FreenetURI;
46
47 /**
48  * The Sone core.
49  *
50  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
51  */
52 public class Core implements IdentityListener {
53
54         /**
55          * Enumeration for the possible states of a {@link Sone}.
56          *
57          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
58          */
59         public enum SoneStatus {
60
61                 /** The Sone is unknown, i.e. not yet downloaded. */
62                 unknown,
63
64                 /** The Sone is idle, i.e. not being downloaded or inserted. */
65                 idle,
66
67                 /** The Sone is currently being inserted. */
68                 inserting,
69
70                 /** The Sone is currently being downloaded. */
71                 downloading,
72         }
73
74         /** The logger. */
75         private static final Logger logger = Logging.getLogger(Core.class);
76
77         /** The options. */
78         private final Options options = new Options();
79
80         /** The configuration. */
81         private final Configuration configuration;
82
83         /** The identity manager. */
84         private final IdentityManager identityManager;
85
86         /** Interface to freenet. */
87         private final FreenetInterface freenetInterface;
88
89         /** The Sone downloader. */
90         private final SoneDownloader soneDownloader;
91
92         /** The Sones’ statuses. */
93         /* synchronize access on itself. */
94         private final Map<Sone, SoneStatus> soneStatuses = new HashMap<Sone, SoneStatus>();
95
96         /** Sone inserters. */
97         /* synchronize access on this on localSones. */
98         private final Map<Sone, SoneInserter> soneInserters = new HashMap<Sone, SoneInserter>();
99
100         /** All local Sones. */
101         /* synchronize access on this on itself. */
102         private Map<String, Sone> localSones = new HashMap<String, Sone>();
103
104         /** All remote Sones. */
105         /* synchronize access on this on itself. */
106         private Map<String, Sone> remoteSones = new HashMap<String, Sone>();
107
108         /** All posts. */
109         private Map<String, Post> posts = new HashMap<String, Post>();
110
111         /** All replies. */
112         private Map<String, Reply> replies = new HashMap<String, Reply>();
113
114         /**
115          * Creates a new core.
116          *
117          * @param configuration
118          *            The configuration of the core
119          * @param freenetInterface
120          *            The freenet interface
121          * @param identityManager
122          *            The identity manager
123          */
124         public Core(Configuration configuration, FreenetInterface freenetInterface, IdentityManager identityManager) {
125                 this.configuration = configuration;
126                 this.freenetInterface = freenetInterface;
127                 this.identityManager = identityManager;
128                 this.soneDownloader = new SoneDownloader(this, freenetInterface);
129         }
130
131         //
132         // ACCESSORS
133         //
134
135         /**
136          * Returns the options used by the core.
137          *
138          * @return The options of the core
139          */
140         public Options getOptions() {
141                 return options;
142         }
143
144         /**
145          * Returns the identity manager used by the core.
146          *
147          * @return The identity manager
148          */
149         public IdentityManager getIdentityManager() {
150                 return identityManager;
151         }
152
153         /**
154          * Returns the status of the given Sone.
155          *
156          * @param sone
157          *            The Sone to get the status for
158          * @return The status of the Sone
159          */
160         public SoneStatus getSoneStatus(Sone sone) {
161                 synchronized (soneStatuses) {
162                         return soneStatuses.get(sone);
163                 }
164         }
165
166         /**
167          * Sets the status of the given Sone.
168          *
169          * @param sone
170          *            The Sone to set the status of
171          * @param soneStatus
172          *            The status to set
173          */
174         public void setSoneStatus(Sone sone, SoneStatus soneStatus) {
175                 synchronized (soneStatuses) {
176                         soneStatuses.put(sone, soneStatus);
177                 }
178         }
179
180         /**
181          * Returns all Sones, remote and local.
182          *
183          * @return All Sones
184          */
185         public Set<Sone> getSones() {
186                 Set<Sone> allSones = new HashSet<Sone>();
187                 allSones.addAll(getLocalSones());
188                 allSones.addAll(getRemoteSones());
189                 return allSones;
190         }
191
192         /**
193          * Returns the Sone with the given ID, regardless whether it’s local or
194          * remote.
195          *
196          * @param id
197          *            The ID of the Sone to get
198          * @return The Sone with the given ID, or {@code null} if there is no such
199          *         Sone
200          */
201         public Sone getSone(String id) {
202                 Sone sone = getRemoteSone(id);
203                 if (sone != null) {
204                         return sone;
205                 }
206                 sone = getLocalSone(id);
207                 return sone;
208         }
209
210         /**
211          * Returns whether the given Sone is a local Sone.
212          *
213          * @param sone
214          *            The Sone to check for its locality
215          * @return {@code true} if the given Sone is local, {@code false} otherwise
216          */
217         public boolean isLocalSone(Sone sone) {
218                 synchronized (localSones) {
219                         return localSones.containsKey(sone.getId());
220                 }
221         }
222
223         /**
224          * Returns all local Sones.
225          *
226          * @return All local Sones
227          */
228         public Set<Sone> getLocalSones() {
229                 synchronized (localSones) {
230                         return new HashSet<Sone>(localSones.values());
231                 }
232         }
233
234         /**
235          * Returns the local Sone with the given ID.
236          *
237          * @param id
238          *            The ID of the Sone to get
239          * @return The Sone, or {@code null} if there is no Sone with the given ID
240          */
241         public Sone getLocalSone(String id) {
242                 synchronized (localSones) {
243                         return localSones.get(id);
244                 }
245         }
246
247         /**
248          * Returns all remote Sones.
249          *
250          * @return All remote Sones
251          */
252         public Set<Sone> getRemoteSones() {
253                 synchronized (remoteSones) {
254                         return new HashSet<Sone>(remoteSones.values());
255                 }
256         }
257
258         /**
259          * Returns the remote Sone with the given ID.
260          *
261          * @param id
262          *            The ID of the remote Sone to get
263          * @return The Sone, or {@code null} if there is no such Sone
264          */
265         public Sone getRemoteSone(String id) {
266                 synchronized (remoteSones) {
267                         return remoteSones.get(id);
268                 }
269         }
270
271         /**
272          * Returns whether the given Sone is a remote Sone.
273          *
274          * @param sone
275          *            The Sone to check
276          * @return {@code true} if the given Sone is a remote Sone, {@code false}
277          *         otherwise
278          */
279         public boolean isRemoteSone(Sone sone) {
280                 synchronized (remoteSones) {
281                         return remoteSones.containsKey(sone.getId());
282                 }
283         }
284
285         /**
286          * Returns the post with the given ID.
287          *
288          * @param postId
289          *            The ID of the post to get
290          * @return The post, or {@code null} if there is no such post
291          */
292         public Post getPost(String postId) {
293                 synchronized (posts) {
294                         return posts.get(postId);
295                 }
296         }
297
298         /**
299          * Returns the reply with the given ID.
300          *
301          * @param replyId
302          *            The ID of the reply to get
303          * @return The reply, or {@code null} if there is no such reply
304          */
305         public Reply getReply(String replyId) {
306                 synchronized (replies) {
307                         return replies.get(replyId);
308                 }
309         }
310
311         /**
312          * Returns all replies for the given post, order ascending by time.
313          *
314          * @param post
315          *            The post to get all replies for
316          * @return All replies for the given post
317          */
318         public List<Reply> getReplies(Post post) {
319                 Set<Sone> sones = getSones();
320                 List<Reply> replies = new ArrayList<Reply>();
321                 for (Sone sone : sones) {
322                         for (Reply reply : sone.getReplies()) {
323                                 if (reply.getPost().equals(post)) {
324                                         replies.add(reply);
325                                 }
326                         }
327                 }
328                 Collections.sort(replies, Reply.TIME_COMPARATOR);
329                 return replies;
330         }
331
332         /**
333          * Returns all Sones that have liked the given post.
334          *
335          * @param post
336          *            The post to get the liking Sones for
337          * @return The Sones that like the given post
338          */
339         public Set<Sone> getLikes(Post post) {
340                 Set<Sone> sones = new HashSet<Sone>();
341                 for (Sone sone : getSones()) {
342                         if (sone.getLikedPostIds().contains(post.getId())) {
343                                 sones.add(sone);
344                         }
345                 }
346                 return sones;
347         }
348
349         /**
350          * Returns all Sones that have liked the given reply.
351          *
352          * @param reply
353          *            The reply to get the liking Sones for
354          * @return The Sones that like the given reply
355          */
356         public Set<Sone> getLikes(Reply reply) {
357                 Set<Sone> sones = new HashSet<Sone>();
358                 for (Sone sone : getSones()) {
359                         if (sone.getLikedPostIds().contains(reply.getId())) {
360                                 sones.add(sone);
361                         }
362                 }
363                 return sones;
364         }
365
366         //
367         // ACTIONS
368         //
369
370         /**
371          * Adds a local Sone from the given ID which has to be the ID of an own
372          * identity.
373          *
374          * @param id
375          *            The ID of an own identity to add a Sone for
376          * @return The added (or already existing) Sone
377          */
378         public Sone addLocalSone(String id) {
379                 synchronized (localSones) {
380                         if (localSones.containsKey(id)) {
381                                 logger.log(Level.FINE, "Tried to add known local Sone: %s", id);
382                                 return localSones.get(id);
383                         }
384                         OwnIdentity ownIdentity = identityManager.getOwnIdentity(id);
385                         if (ownIdentity == null) {
386                                 logger.log(Level.INFO, "Invalid Sone ID: %s", id);
387                                 return null;
388                         }
389                         return addLocalSone(ownIdentity);
390                 }
391         }
392
393         /**
394          * Adds a local Sone from the given own identity.
395          *
396          * @param ownIdentity
397          *            The own identity to create a Sone from
398          * @return The added (or already existing) Sone
399          */
400         public Sone addLocalSone(OwnIdentity ownIdentity) {
401                 if (ownIdentity == null) {
402                         logger.log(Level.WARNING, "Given OwnIdentity is null!");
403                         return null;
404                 }
405                 synchronized (localSones) {
406                         if (localSones.containsKey(ownIdentity.getId())) {
407                                 logger.log(Level.FINE, "Tried to add known local Sone: %s", ownIdentity);
408                                 return localSones.get(ownIdentity.getId());
409                         }
410                         final Sone sone;
411                         try {
412                                 sone = new Sone(ownIdentity).setInsertUri(new FreenetURI(ownIdentity.getInsertUri())).setRequestUri(new FreenetURI(ownIdentity.getRequestUri()));
413                                 sone.setLatestEdition(Numbers.safeParseLong(ownIdentity.getProperty("Sone.LatestEdition"), (long) 0));
414                         } catch (MalformedURLException mue1) {
415                                 logger.log(Level.SEVERE, "Could not convert the Identity’s URIs to Freenet URIs: " + ownIdentity.getInsertUri() + ", " + ownIdentity.getRequestUri(), mue1);
416                                 return null;
417                         }
418                         /* TODO - load posts ’n stuff */
419                         localSones.put(ownIdentity.getId(), sone);
420                         SoneInserter soneInserter = new SoneInserter(this, freenetInterface, sone);
421                         soneInserters.put(sone, soneInserter);
422                         soneInserter.start();
423                         setSoneStatus(sone, SoneStatus.idle);
424                         new Thread(new Runnable() {
425
426                                 @Override
427                                 @SuppressWarnings("synthetic-access")
428                                 public void run() {
429                                         soneDownloader.fetchSone(sone);
430                                 }
431
432                         }, "Sone Downloader").start();
433                         return sone;
434                 }
435         }
436
437         /**
438          * Creates a new Sone for the given own identity.
439          *
440          * @param ownIdentity
441          *            The own identity to create a Sone for
442          * @return The created Sone
443          */
444         public Sone createSone(OwnIdentity ownIdentity) {
445                 identityManager.addContext(ownIdentity, "Sone");
446                 Sone sone = addLocalSone(ownIdentity);
447                 synchronized (sone) {
448                         /* mark as modified so that it gets inserted immediately. */
449                         sone.setModificationCounter(sone.getModificationCounter() + 1);
450                 }
451                 return sone;
452         }
453
454         /**
455          * Adds the Sone of the given identity.
456          *
457          * @param identity
458          *            The identity whose Sone to add
459          * @return The added or already existing Sone
460          */
461         public Sone addRemoteSone(Identity identity) {
462                 if (identity == null) {
463                         logger.log(Level.WARNING, "Given Identity is null!");
464                         return null;
465                 }
466                 synchronized (remoteSones) {
467                         if (remoteSones.containsKey(identity.getId())) {
468                                 logger.log(Level.FINE, "Identity already exists: %s", identity);
469                                 return remoteSones.get(identity.getId());
470                         }
471                         Sone sone = new Sone(identity);
472                         sone.setRequestUri(getSoneUri(identity.getRequestUri(), identity.getProperty("Sone.LatestEdition")));
473                         remoteSones.put(identity.getId(), sone);
474                         soneDownloader.addSone(sone);
475                         setSoneStatus(sone, SoneStatus.idle);
476                         return sone;
477                 }
478         }
479
480         /**
481          * Updates the stores Sone with the given Sone.
482          *
483          * @param sone
484          *            The updated Sone
485          */
486         public void updateSone(Sone sone) {
487                 if (isRemoteSone(sone)) {
488                         Sone storedSone = getRemoteSone(sone.getId());
489                         if (!(sone.getTime() > storedSone.getTime())) {
490                                 logger.log(Level.FINE, "Downloaded Sone %s is not newer than stored Sone %s.", new Object[] { sone, storedSone });
491                                 return;
492                         }
493                         synchronized (posts) {
494                                 for (Post post : storedSone.getPosts()) {
495                                         posts.remove(post.getId());
496                                 }
497                                 for (Post post : sone.getPosts()) {
498                                         posts.put(post.getId(), post);
499                                 }
500                         }
501                         synchronized (replies) {
502                                 for (Reply reply : storedSone.getReplies()) {
503                                         replies.remove(reply.getId());
504                                 }
505                                 for (Reply reply : sone.getReplies()) {
506                                         replies.put(reply.getId(), reply);
507                                 }
508                         }
509                         synchronized (storedSone) {
510                                 storedSone.setTime(sone.getTime());
511                                 storedSone.setProfile(sone.getProfile());
512                                 storedSone.setPosts(sone.getPosts());
513                                 storedSone.setReplies(sone.getReplies());
514                                 storedSone.setLikePostIds(sone.getLikedPostIds());
515                                 storedSone.setLikeReplyIds(sone.getLikedReplyIds());
516                                 storedSone.setLatestEdition(sone.getRequestUri().getEdition());
517                         }
518                         saveSone(storedSone);
519                 }
520         }
521
522         /**
523          * Deletes the given Sone. This will remove the Sone from the
524          * {@link #getLocalSone(String) local Sones}, stops its {@link SoneInserter}
525          * and remove the context from its identity.
526          *
527          * @param sone
528          *            The Sone to delete
529          */
530         public void deleteSone(Sone sone) {
531                 if (!(sone.getIdentity() instanceof OwnIdentity)) {
532                         logger.log(Level.WARNING, "Tried to delete Sone of non-own identity: %s", sone);
533                         return;
534                 }
535                 synchronized (localSones) {
536                         if (!localSones.containsKey(sone.getId())) {
537                                 logger.log(Level.WARNING, "Tried to delete non-local Sone: %s", sone);
538                                 return;
539                         }
540                         localSones.remove(sone.getId());
541                         soneInserters.remove(sone.getId()).stop();
542                 }
543                 identityManager.removeContext((OwnIdentity) sone.getIdentity(), "Sone");
544         }
545
546         /**
547          * Saves the given Sone. This will persist all local settings for the given
548          * Sone, such as the friends list and similar, private options.
549          *
550          * @param sone
551          *            The Sone to save
552          */
553         public void saveSone(Sone sone) {
554                 if (!isLocalSone(sone)) {
555                         logger.log(Level.FINE, "Tried to save non-local Sone: %s", sone);
556                         return;
557                 }
558                 if (!(sone.getIdentity() instanceof OwnIdentity)) {
559                         logger.log(Level.WARNING, "Local Sone without OwnIdentity found, refusing to save: %s", sone);
560                         return;
561                 }
562                 identityManager.setProperty((OwnIdentity) sone.getIdentity(), "Sone.LatestEdition", String.valueOf(sone.getLatestEdition()));
563                 /* TODO - implement saving. */
564         }
565
566         /**
567          * Creates a new post.
568          *
569          * @param sone
570          *            The Sone that creates the post
571          * @param text
572          *            The text of the post
573          */
574         public void createPost(Sone sone, String text) {
575                 createPost(sone, System.currentTimeMillis(), text);
576         }
577
578         /**
579          * Creates a new post.
580          *
581          * @param sone
582          *            The Sone that creates the post
583          * @param time
584          *            The time of the post
585          * @param text
586          *            The text of the post
587          */
588         public void createPost(Sone sone, long time, String text) {
589                 if (!isLocalSone(sone)) {
590                         logger.log(Level.FINE, "Tried to create post for non-local Sone: %s", sone);
591                         return;
592                 }
593                 Post post = new Post(sone, time, text);
594                 synchronized (posts) {
595                         posts.put(post.getId(), post);
596                 }
597                 sone.addPost(post);
598                 saveSone(sone);
599         }
600
601         /**
602          * Deletes the given post.
603          *
604          * @param post
605          *            The post to delete
606          */
607         public void deletePost(Post post) {
608                 if (!isLocalSone(post.getSone())) {
609                         logger.log(Level.WARNING, "Tried to delete post of non-local Sone: %s", post.getSone());
610                         return;
611                 }
612                 post.getSone().removePost(post);
613                 synchronized (posts) {
614                         posts.remove(post.getId());
615                 }
616                 saveSone(post.getSone());
617         }
618
619         /**
620          * Creates a new reply.
621          *
622          * @param sone
623          *            The Sone that creates the reply
624          * @param post
625          *            The post that this reply refers to
626          * @param text
627          *            The text of the reply
628          */
629         public void createReply(Sone sone, Post post, String text) {
630                 createReply(sone, post, System.currentTimeMillis(), text);
631         }
632
633         /**
634          * Creates a new reply.
635          *
636          * @param sone
637          *            The Sone that creates the reply
638          * @param post
639          *            The post that this reply refers to
640          * @param time
641          *            The time of the reply
642          * @param text
643          *            The text of the reply
644          */
645         public void createReply(Sone sone, Post post, long time, String text) {
646                 if (!isLocalSone(sone)) {
647                         logger.log(Level.FINE, "Tried to create reply for non-local Sone: %s", sone);
648                         return;
649                 }
650                 Reply reply = new Reply(sone, post, System.currentTimeMillis(), text);
651                 synchronized (replies) {
652                         replies.put(reply.getId(), reply);
653                 }
654                 sone.addReply(reply);
655                 saveSone(sone);
656         }
657
658         /**
659          * Deletes the given reply.
660          *
661          * @param reply
662          *            The reply to delete
663          */
664         public void deleteReply(Reply reply) {
665                 Sone sone = reply.getSone();
666                 if (!isLocalSone(sone)) {
667                         logger.log(Level.FINE, "Tried to delete non-local reply: %s", reply);
668                         return;
669                 }
670                 synchronized (replies) {
671                         replies.remove(reply.getId());
672                 }
673                 sone.removeReply(reply);
674                 saveSone(sone);
675         }
676
677         /**
678          * Starts the core.
679          */
680         public void start() {
681                 loadConfiguration();
682         }
683
684         /**
685          * Stops the core.
686          */
687         public void stop() {
688                 synchronized (localSones) {
689                         for (SoneInserter soneInserter : soneInserters.values()) {
690                                 soneInserter.stop();
691                         }
692                 }
693                 saveConfiguration();
694         }
695
696         //
697         // PRIVATE METHODS
698         //
699
700         /**
701          * Loads the configuration.
702          */
703         @SuppressWarnings("unchecked")
704         private void loadConfiguration() {
705                 /* create options. */
706                 options.addIntegerOption("InsertionDelay", new DefaultOption<Integer>(60, new OptionWatcher<Integer>() {
707
708                         @Override
709                         public void optionChanged(Option<Integer> option, Integer oldValue, Integer newValue) {
710                                 SoneInserter.setInsertionDelay(newValue);
711                         }
712
713                 }));
714                 options.addBooleanOption("ClearOnNextRestart", new DefaultOption<Boolean>(false));
715                 options.addBooleanOption("ReallyClearOnNextRestart", new DefaultOption<Boolean>(false));
716
717                 /* read options from configuration. */
718                 options.getBooleanOption("ClearOnNextRestart").set(configuration.getBooleanValue("Option/ClearOnNextRestart").getValue(null));
719                 options.getBooleanOption("ReallyClearOnNextRestart").set(configuration.getBooleanValue("Option/ReallyClearOnNextRestart").getValue(null));
720                 boolean clearConfiguration = options.getBooleanOption("ClearOnNextRestart").get() && options.getBooleanOption("ReallyClearOnNextRestart").get();
721                 options.getBooleanOption("ClearOnNextRestart").set(null);
722                 options.getBooleanOption("ReallyClearOnNextRestart").set(null);
723                 if (clearConfiguration) {
724                         /* stop loading the configuration. */
725                         return;
726                 }
727
728                 options.getIntegerOption("InsertionDelay").set(configuration.getIntValue("Option/InsertionDelay").getValue(null));
729
730         }
731
732         /**
733          * Saves the current options.
734          */
735         private void saveConfiguration() {
736                 /* store the options first. */
737                 try {
738                         configuration.getIntValue("Option/InsertionDelay").setValue(options.getIntegerOption("InsertionDelay").getReal());
739                         configuration.getBooleanValue("Option/ClearOnNextRestart").setValue(options.getBooleanOption("ClearOnNextRestart").getReal());
740                         configuration.getBooleanValue("Option/ReallyClearOnNextRestart").setValue(options.getBooleanOption("ReallyClearOnNextRestart").getReal());
741                 } catch (ConfigurationException ce1) {
742                         logger.log(Level.SEVERE, "Could not store configuration!", ce1);
743                 }
744         }
745
746         /**
747          * Generate a Sone URI from the given URI and latest edition.
748          *
749          * @param uriString
750          *            The URI to derive the Sone URI from
751          * @param latestEditionString
752          *            The latest edition as a {@link String}, or {@code null}
753          * @return The derived URI
754          */
755         private FreenetURI getSoneUri(String uriString, String latestEditionString) {
756                 try {
757                         FreenetURI uri = new FreenetURI(uriString).setDocName("Sone").setMetaString(new String[0]).setSuggestedEdition(Numbers.safeParseLong(latestEditionString, (long) 0));
758                         return uri;
759                 } catch (MalformedURLException mue1) {
760                         logger.log(Level.WARNING, "Could not create Sone URI from URI: " + uriString, mue1);
761                         return null;
762                 }
763         }
764
765         //
766         // INTERFACE IdentityListener
767         //
768
769         /**
770          * {@inheritDoc}
771          */
772         @Override
773         public void ownIdentityAdded(OwnIdentity ownIdentity) {
774                 logger.log(Level.FINEST, "Adding OwnIdentity: " + ownIdentity);
775                 if (ownIdentity.hasContext("Sone")) {
776                         addLocalSone(ownIdentity);
777                 }
778         }
779
780         /**
781          * {@inheritDoc}
782          */
783         @Override
784         public void ownIdentityRemoved(OwnIdentity ownIdentity) {
785                 /* TODO */
786         }
787
788         /**
789          * {@inheritDoc}
790          */
791         @Override
792         public void identityAdded(Identity identity) {
793                 logger.log(Level.FINEST, "Adding Identity: " + identity);
794                 addRemoteSone(identity);
795         }
796
797         /**
798          * {@inheritDoc}
799          */
800         @Override
801         public void identityUpdated(Identity identity) {
802                 /* TODO */
803         }
804
805         /**
806          * {@inheritDoc}
807          */
808         @Override
809         public void identityRemoved(Identity identity) {
810                 /* TODO */
811         }
812
813 }