KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > sync4j > framework > protocol > ProtocolUtil


1 /**
2  * Copyright (C) 2003-2005 Funambol
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 2 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, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  */

18 package sync4j.framework.protocol;
19
20 import java.util.*;
21
22 import sync4j.framework.core.*;
23 import sync4j.framework.database.Database;
24 import sync4j.framework.protocol.v11.*;
25 import sync4j.framework.tools.MD5;
26
27 /**
28  *
29  * @author Stefano Fornari @ Funambol
30  * @author Harrie Hazewinkel
31  *
32  * @version $Id: ProtocolUtil.java,v 1.18 2005/07/14 16:52:11 nichele Exp $
33  *
34  */

35 public class ProtocolUtil {
36
37     /** Filters the given commands based on the given class
38      *
39      * @param commands commands to be filtered
40      * @param filterClass selector
41      *
42      * @return a java.util.List containing the selected commands
43      *
44      */

45     public static ArrayList filterCommands(AbstractCommand[] commands, Class JavaDoc filterClass) {
46         ArrayList filteredCommands = new ArrayList();
47
48         for (int i=0; i<commands.length; ++i) {
49             if (filterClass.isInstance(commands[i])) {
50                 filteredCommands.add(commands[i]);
51             }
52         }
53
54         return filteredCommands;
55     }
56
57     /**
58      * Filters the given commands based on the given command id. A command is
59      * selected if it a ResponseCommand (only ResponseCommand subclasses have
60      * got the reference command id) and its command id ref matches <i>cmdId</i>.
61      *
62      * @param commands commands to be filtered
63      * @param cmdId selector
64      *
65      * @return a java.util.List containing the selected commands
66      *
67      */

68     public static ArrayList filterCommands(AbstractCommand[] commands,
69                                            CmdID cmdId ) {
70         ArrayList filteredCommands = new ArrayList();
71
72         for (int i=0; i<commands.length; ++i) {
73             if ( (commands[i] instanceof ResponseCommand)
74                && ((((ResponseCommand)commands[i]).getCmdID()).equals(cmdId))) {
75                 filteredCommands.add(commands[i]);
76             }
77         }
78
79         return filteredCommands;
80     }
81
82     /**
83      * Filters the given commands based on the given command type and command id.
84      * It combines <i>filterCommands(commands, filterClass)</i> and
85      * <i>filterCommands(commands, cmdId)</i> returning only the commands
86      * that respect both requirements.
87      *
88      * @param commands commands to be filtered
89      * @param filterClass class type selector
90      * @param cmdId selector
91      *
92      * @return a java.util.List containing the selected commands
93      *
94      */

95     public static ArrayList filterCommands(AbstractCommand[] commands ,
96                                            Class JavaDoc filterClass,
97                                            CmdID cmdId ) {
98         //
99
// Since filtering on command identifier seems more selective,
100
// filterCommands(commands, cmdId) is called first and than
101
// filterCommands(..., filterClass) is called with the returned values.
102
//
103
ArrayList list = filterCommands(commands, cmdId);
104         int size = list.size();
105         AbstractCommand [] aCommands = new AbstractCommand[size];
106         for (int i=0; i < size; i++) {
107             aCommands[i] = (AbstractCommand)list.get(i);
108         }
109         return filterCommands(
110                     /* not compatible with j2me
111                     (AbstractCommand[])list.toArray(new AbstractCommand[0]),
112                     */

113                     aCommands,
114                    filterClass
115                );
116     }
117
118     /**
119      * Filters a list of commands extracting the ones of the given types.
120      *
121      * @param commands the list of command to be filtered
122      * @param types the command types to extract
123      *
124      * @return an array of the selected commmands
125      */

126     public static List filterCommands(List commands, String JavaDoc[] types) {
127         StringBuffer JavaDoc sb = new StringBuffer JavaDoc(",");
128
129         for (int i = 0; ((types != null) && (i < types.length)); ++i) {
130             sb.append(types[i]).append(',');
131         }
132
133         ArrayList selectedCommands = new ArrayList();
134         AbstractCommand command = null;
135         Iterator i = commands.iterator();
136         while (i.hasNext()) {
137             command = (AbstractCommand) i.next();
138
139             if (sb.indexOf(',' + command.getName() + ',') >= 0) {
140                 selectedCommands.add(command);
141             }
142         }
143
144         return selectedCommands;
145     }
146
147     /**
148      * Filters the given commands based on the given command type and command name.
149      * It combines <i>filterCommands(commands, filterClass)</i> and
150      * <i>filterCommands(commands, cmdId)</i> returning only the commands
151      * that respect both requirements.
152      *
153      * @param commands commands to be filtered
154      * @param filterClass class type selector
155      * @param cmd the command name selector
156      *
157      * @return a java.util.List containing the selected commands
158      *
159      */

160     public static AbstractCommand filterCommands(AbstractCommand[] commands,
161                                                  Class JavaDoc filterClass ,
162                                                  String JavaDoc cmd ) {
163
164         ArrayList all = filterCommands(commands, filterClass);
165         for (int i=0; i<all.size(); ++i) {
166             if (((Status)all.get(i)).getCmd().equals(cmd)) {
167                 return (Status)all.get(i);
168             }
169         }
170         return null;
171     }
172
173     /**
174      * Filters the given commands returning all commands of all types but the
175      * type corresponding to the given class
176      *
177      * @param commands commands to be filtered
178      * @param filterClass selector
179      *
180      * @return a java.util.List containing the selected commands
181      *
182      */

183     public static ArrayList inverseFilterCommands(AbstractCommand[] commands, Class JavaDoc filterClass) {
184         ArrayList filteredCommands = new ArrayList();
185
186         for (int i=0; i<commands.length; ++i) {
187             if (!filterClass.isInstance(commands[i])) {
188                 filteredCommands.add(commands[i]);
189             }
190         }
191
192         return filteredCommands;
193     }
194
195     /**
196      * Creates and returns and AlertCommand for the synchronization of the
197      * given database.
198      *
199      * @param id the command id - NULL
200      * @param noResponse
201      * @param credential - NULL
202      * @param db the database to be synchronized - NOT NULL
203      *
204      * @return the AlertCommand object
205      */

206     public static Alert createAlertCommand(CmdID id ,
207                                            boolean noResponse,
208                                            Cred credential,
209                                            Database db ) {
210         Item[] items = new Item[1];
211         Anchor serverAnchor = db.getServerAnchor();
212         Meta meta = new Meta();
213         meta.setAnchor(serverAnchor);
214         items[0] = new Item(db.getTarget(),
215                             db.getSource(),
216                             meta ,
217                             null , //data
218
false ); //MoreData
219

220         return new Alert(
221                    id ,
222                    noResponse ,
223                    credential ,
224                    db.getMethod(),
225                    items
226                );
227     }
228
229     /**
230      * Translates a Target object to a Source object
231      *
232      * @param target the target object - NULL
233      *
234      * @return a Source object with the same URI and local name of <i>target</i>
235      */

236     public static Source target2Source(Target target) {
237         if (target == null) {
238             return null;
239         }
240         return new Source(target.getLocURI(), target.getLocName());
241     }
242
243     /**
244      * Translates a Source object to a Target object
245      *
246      * @param source the source object - NULL
247      *
248      * @return a Target object with the same URI and local name of <i>source</i>
249      */

250     public static Target source2Target(Source source) {
251         if (source == null) {
252             return null;
253         }
254         return new Target(source.getLocURI(), source.getLocName());
255     }
256
257     /**
258      * Extracts the target and source refs from an array of items
259      *
260      * @param items the items to inspect. If null targetRefs and sourceRefs
261      * remain unchanged
262      * @param targetRefs a reference to an array that will contain the references
263      * to the items' targets
264      * @param sourceRefs a reference to an array that will contain the references
265      * to the items' sources
266      *
267      */

268     public static void extractRefs(Item[] items ,
269                                    TargetRef[] targetRefs,
270                                    SourceRef[] sourceRefs) {
271         if (items == null) {
272             return;
273         }
274
275         Target t = null;
276         Source s = null;
277         for (int i=0; i<items.length; ++i) {
278             t = items[i].getTarget();
279             s = items[i].getSource();
280
281             targetRefs[i] = (t != null) ? new TargetRef(t) : null;
282             sourceRefs[i] = (s != null) ? new SourceRef(s) : null;
283         }
284     }
285
286     /**
287      * Checks if a message require a response.<p>
288      * A message requires a response if its body contains commands other than:
289      * <ul>
290      * <li>Status</i>
291      * <li>Map</i>
292      * </ul>
293      *
294      * @param msg the message to check - NOT NULL and properly constructed
295      *
296      * @return true if the message requires a response, false otherwise
297      *
298      */

299     public static boolean noMoreResponse(SyncML msg) {
300         AbstractCommand[] commands =
301         (AbstractCommand[])msg.getSyncBody().getCommands().toArray(
302         new AbstractCommand[0]);
303
304         for(int i=0; ((commands != null) && (i<commands.length)); ++i) {
305             if (!((commands[i] instanceof Status) ||
306                   (commands[i] instanceof sync4j.framework.core.Map))) {
307                 return false;
308             }
309         }
310
311         return true;
312     }
313
314     /**
315      * Generate the next nonce for MD5 authentication
316      * The nextNonce for the session has a number format;
317      * the nextNonce for the chal element is encoding b64
318      *
319      * @return NextNonce a new NextNonce object
320      */

321     public static NextNonce generateNextNonce() {
322         return new NextNonce(MD5.getNextNonce());
323     }
324
325     /**
326      * Returns the header status code of the given message, if specified.<br<
327      * The header status code is the first status in the message body. The
328      * first message of PCK1 has not any header status code. In this case -1 is
329      * returned.
330      *
331      * @param msg the SyncML message object
332      *
333      * @return the header status code or -1 if the given message does not
334      * containno any header status command.
335      */

336     public static int getHeaderStatusCode(SyncML msg) {
337         ArrayList cmdList = msg.getSyncBody().getCommands();
338
339         cmdList = filterCommands(
340                       (AbstractCommand[])cmdList.toArray(new AbstractCommand[cmdList.size()]),
341                       Status.class,
342                       new CmdID("1")
343                   );
344
345         if (cmdList.size() == 0) {
346             return -1;
347         }
348
349         return ((Status)cmdList.get(0)).getStatusCode();
350     }
351
352     /**
353      * Returns the Chal element included in the header status if there is any or
354      * null if no chal is given.
355      *
356      * @param msg the SyncML message object
357      *
358      * @return Chal element included in the header status if there is any or
359      * null if no chal is given.
360      */

361     public static Chal getStatusChal(SyncML msg) {
362         ArrayList cmdList = msg.getSyncBody().getCommands();
363
364         cmdList = filterCommands(
365                       (AbstractCommand[])cmdList.toArray(new AbstractCommand[cmdList.size()]),
366                       Status.class,
367                       new CmdID("1")
368                   );
369
370         if (cmdList.size() == 0) {
371             return null;
372         }
373
374         return ((Status)cmdList.get(0)).getChal();
375     }
376
377     /**
378      * Returns the DevInf (Device Information) element.
379      *
380      * @param msg the SyncML message object
381      *
382      * @return DevInf elements
383      */

384     public static DevInf getDevInf(SyncML msg) {
385         ArrayList cmdList = msg.getSyncBody().getCommands();
386
387         cmdList = filterCommands(
388         (AbstractCommand[])cmdList.toArray(new AbstractCommand[cmdList.size()]),
389         Put.class
390         );
391
392         if (cmdList.size() <= 0) {
393             return null;
394         }
395
396         ArrayList itemList = ((Put)cmdList.get(0)).getItems();
397
398         if (itemList == null) {
399             return null;
400         }
401
402         DevInfItem devInfItem = (DevInfItem)itemList.get(0);
403         if (devInfItem == null) {
404             return null;
405         }
406         return devInfItem.getDevInfData().getDevInf();
407     }
408
409     /*
410      * Returns item that contains large object (moreData == true).
411      * Only last command can have more data.
412      *
413      * @param commands AbstractCommand[]
414      * @return item that contains large object or null if
415      * there aren't items with moreData == true
416      */

417     public static Item getLargeObject(List commands) {
418         if (commands.size() == 0) {
419             return null;
420         }
421
422         AbstractCommand command = null;
423         ArrayList items = null;
424         Item item = null;
425
426         command = (AbstractCommand)(commands.get(commands.size() -1));
427
428         if (!(command instanceof ItemizedCommand)) {
429             return null;
430         }
431         items = ((ItemizedCommand)command).getItems();
432         item = (Item)items.get(items.size() - 1);
433         if (item.isMoreData()) {
434             return item;
435         }
436         return null;
437     }
438
439     /**
440      * Checks if the given command contains a item with more data
441      * @param command AbstractCommand
442      * @return true if the given command contains a item with more data, false otherwise
443      */

444     public static boolean hasLargeObject(AbstractCommand command) {
445         if (! (command instanceof ItemizedCommand)) {
446             return false;
447         }
448         ArrayList items = ((ItemizedCommand)command).getItems();
449         Iterator iItems = items.iterator();
450         Item item = null;
451         while (iItems.hasNext()) {
452             item = (Item)iItems.next();
453             if (item.isMoreData()) {
454                 return true;
455             }
456         }
457         return false;
458     }
459
460     /**
461      *
462      */

463     public static Item getSyncItem(Sync[] syncs, String JavaDoc itemPath) {
464         String JavaDoc uri = null;
465         int i = 0;
466         for (i=0; i<syncs.length; ++i) {
467             uri = syncs[i].getTarget().getLocURI() + '/';
468             if (itemPath.startsWith(uri)) {
469                 break;
470             }
471         }
472
473         if (i == syncs.length) {
474             return null;
475         }
476
477         String JavaDoc test = null;
478         Iterator j = syncs[i].getCommands().iterator();
479         while (j.hasNext()) {
480             Item item = (Item)((ItemizedCommand)j.next()).getItems().get(0);
481
482             test = uri
483                  + ( (item.getSource() != null)
484                    ? item.getSource().getLocURI()
485                    : item.getTarget().getLocURI())
486                  ;
487
488             if (test.equals(itemPath)) {
489                 return item;
490             }
491         }
492
493         return null;
494     }
495
496     /**
497      * Sort an array of Status object in according to their cmdRef
498      *
499      * @param statusToSort an array of Status object
500      *
501      * @return an array of sorted Status object
502      */

503     public static AbstractCommand[] sortStatusCommand(AbstractCommand[] statusToSort) {
504         StatusComparator comparator = new StatusComparator();
505         Arrays.sort(statusToSort, comparator);
506         return statusToSort;
507     }
508
509     /**
510      * Sort an array of Status object in according to their cmdRef
511      *
512      * @param statusToSort an array of Status object
513      *
514      * @return an array of sorted Status object
515      */

516     public static AbstractCommand[] sortStatusCommand(List statusToSort) {
517         AbstractCommand[] array =
518             (AbstractCommand[])statusToSort.toArray(new AbstractCommand[statusToSort.size()]);
519
520         StatusComparator comparator = new StatusComparator();
521         Arrays.sort(array, comparator);
522         return array;
523     }
524
525     /**
526      * Checks if the given message has any initialization element.
527      * Initialization elements are:
528      * <ul>
529      * <li>SyncHeader (credentials)</li>
530      * <li>Put (client capabilities)</li>
531      * <li>Get (server capabilities)</li>
532      * <li>Alert (database alerting)</li>
533      * </ul>
534      *
535      * @param msg the message
536      *
537      * @return true if the message contains initialization elements, false otherwise.
538      */

539     public static boolean isInitMessage(SyncML msg) {
540
541         Iterator i = msg.getSyncBody().getCommands().iterator();
542         while(i.hasNext()) {
543             AbstractCommand c = (AbstractCommand)i.next();
544
545             if ((c instanceof Put) || (c instanceof Get)) {
546                 return true;
547             } else if (c instanceof Alert) {
548                 //
549
// Alert 222 is not a init command
550
//
551
if (((Alert)c).getData() != AlertCode.NEXT_MESSAGE) {
552                     return true;
553                 }
554             }
555         }
556
557         return false;
558     }
559
560     /**
561      * Checks if the given message has any sync command. Sync commands are:
562      * <ul>
563      * <li>Sync</li>
564      * <li>Add</li>
565      * <li>Replace</li>
566      * <li>Delete</li>
567      * <li>Alert 222</li>
568      * </ul>
569      *
570      * @param msg the message
571      *
572      * @return true if the message contains at least one sync elements, false otherwise.
573      */

574     public static boolean isSyncMessage(SyncML msg) {
575         Iterator i = msg.getSyncBody().getCommands().iterator();
576         while(i.hasNext()) {
577             AbstractCommand c = (AbstractCommand)i.next();
578
579             if ((c instanceof Sync) ||
580                 (c instanceof Add) ||
581                 (c instanceof Replace) ||
582                 (c instanceof Delete) ) {
583                 return true;
584             }
585
586             if ((c instanceof Alert) &&
587                 (((Alert)c).getData() == AlertCode.NEXT_MESSAGE)) {
588                 return true;
589             }
590         }
591
592         return false;
593     }
594
595     /**
596      * Checks if the given message has any Map command.
597      *
598      * @param msg the message
599      *
600      * @return true if the message contains at least one Map elements, false otherwise.
601      */

602     public static boolean isMapMessage(SyncML msg) {
603         Iterator i = msg.getSyncBody().getCommands().iterator();
604         while(i.hasNext()) {
605             AbstractCommand c = (AbstractCommand)i.next();
606
607             if (c instanceof sync4j.framework.core.Map) {
608                 return true;
609             }
610         }
611
612         return false;
613     }
614
615     /**
616      * This method assures that the given list of commands will contain only
617      * one SyncHdr Status. All other ones will be removed.
618      *
619      * @param commands list of commands
620      */

621     public static void removeHeaderStatus(List commands) {
622         boolean first = true;
623         ArrayList remove = new ArrayList();
624
625         Iterator i = commands.iterator();
626         while (i.hasNext()) {
627             AbstractCommand c = (AbstractCommand)i.next();
628             if (c instanceof Status) {
629                 if (SyncHdr.COMMAND_NAME.equals(((Status)c).getCmd())) {
630                     if (first == true) {
631                         first = false;
632                     } else {
633                         remove.add(c);
634                     }
635                 }
636             }
637         }
638
639         commands.removeAll(remove);
640     }
641
642     /**
643      * Given a list of commands, if there are more than one Sync commands
644      * of the same database, all commands are merged in one.
645      *
646      * @param commands the list of commands to process
647      *
648      */

649     public static void mergeSyncCommands(List commands) {
650         ArrayList remove = new ArrayList();
651
652         List syncs = ProtocolUtil.filterCommands(commands, new String JavaDoc[] { Sync.COMMAND_NAME });
653
654         HashMap h = new HashMap();
655
656         String JavaDoc uri = null;
657         Sync sync1 = null, sync2 = null;
658
659         Iterator i = syncs.iterator();
660         while (i.hasNext()) {
661             sync1 = (Sync)i.next();
662             uri = sync1.getTarget().getLocURI();
663             sync2 = (Sync)h.get(uri);
664             if (sync2 == null) {
665                 h.put(uri, sync1);
666             } else {
667                 sync2.getCommands().addAll(sync1.getCommands());
668                 remove.add(sync1);
669             }
670         }
671
672         commands.removeAll(remove);
673     }
674
675     /**
676      * Returns the Meta Size if specified. If not specified, it returns null.
677      * Note that the meta information can be specified at the command level or
678      * at the item level. In the case of the item level, if there are mmore than
679      * one item, the one that matters is the last one, because it is supposed
680      * that being chunked, it is the last one that fits in the message.
681      * In any case the size specified at the item level overwrite the size at
682      * the command level.
683      *
684      * @param cmd the command to check for the size
685      *
686      * @return the Meta Size if specified, null otherwise
687      */

688     public static Long JavaDoc getLOSize(ModificationCommand cmd) {
689         Meta meta = null;
690         Long JavaDoc size = null;
691
692         //
693
// First check the item.
694
//
695
List items = cmd.getItems();
696         int l = items.size();
697
698         if (l>0) {
699             meta = ((Item)items.get(l-1)).getMeta();
700
701             if (meta != null) {
702                 size = meta.getSize();
703             }
704
705             if (size != null) {
706                 return size;
707             }
708         }
709
710         //
711
// No size found at the item level, let's check the command level
712
//
713
meta = cmd.getMeta();
714
715         return (meta == null) ? null : meta.getSize();
716     }
717
718     /**
719      * Reassign ordered cmdId to all commands in the given list.
720      *
721      * @param commands AbstractCommand[]
722      */

723     public static void updateCmdId(List commandsList) {
724         updateCmdId(commandsList, 1);
725     }
726
727     /**
728      * Reassign ordered cmdId to all commands in the given list.
729      * <br>To the first command is assigned cmdID=startId.
730      *
731      * @param commands List
732      * @param startId the first id to use
733      * @return the last id used + 1
734      */

735     public static int updateCmdId(List commands, int startId) {
736         Iterator iCommand = commands.iterator();
737         AbstractCommand command = null;
738         int id = startId;
739         while (iCommand.hasNext()) {
740             command = (AbstractCommand)iCommand.next();
741             command.setCmdID(new CmdID(id++));
742             if (command instanceof Sync) {
743                 id = updateCmdId( ( (Sync)command).getCommands(), id);
744             } else if (command instanceof Atomic) {
745                 id = updateCmdId( ( (Atomic)command).getCommands(), id);
746             } else if (command instanceof Sequence) {
747                 id = updateCmdId( ( (Sequence)command).getCommands(), id);
748             }
749         }
750         return id;
751     }
752
753     // --------------------------------------------------------- Private methods
754

755     /**
756      * This class compares two Status object in according to their cmdRef
757      */

758     private static class StatusComparator implements java.util.Comparator JavaDoc {
759         private StatusComparator() {
760         }
761
762         public int compare(Object JavaDoc o1, Object JavaDoc o2) {
763
764           Object JavaDoc msgRef1 = null;
765           Object JavaDoc msgRef2 = null;
766
767           Object JavaDoc cmdRef1 = null;
768           Object JavaDoc cmdRef2 = null;
769
770           msgRef1 = new Integer JavaDoc(((Status)o1).getMsgRef());
771           msgRef2 = new Integer JavaDoc(((Status)o2).getMsgRef());
772
773           cmdRef1 = new Integer JavaDoc( ( (Status)o1).getCmdRef());
774           cmdRef2 = new Integer JavaDoc( ( (Status)o2).getCmdRef());
775
776           int msgRefCompare = ((Integer JavaDoc)msgRef1).compareTo((Integer JavaDoc)msgRef2);
777
778           //
779
// msgRef1 == msgRef2
780
//
781
if ( msgRefCompare == 0 ) {
782               return ( (Integer JavaDoc)cmdRef1).compareTo((Integer JavaDoc)cmdRef2);
783           }
784
785           return msgRefCompare;
786       }
787
788     }
789 }
790
Popular Tags