KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > sync4j > framework > engine > dm > Util


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.engine.dm;
19
20 import java.net.URLEncoder JavaDoc;
21 import java.util.*;
22 import java.util.Map JavaDoc;
23
24 import sync4j.framework.core.*;
25 import sync4j.framework.protocol.ProtocolException;
26 import sync4j.framework.tools.Base64;
27 import sync4j.framework.tools.CommandIdGenerator;
28
29 /**
30  * @author Stefano Fornari @ Funambol
31  *
32  * @version $Id: Util.java,v 1.1 2005/05/16 17:32:55 nichele Exp $
33  */

34 public final class Util {
35
36     // --------------------------------------------------------------- Constants
37

38     private static final String JavaDoc MANAGEMENT_COMMANDS
39         = ",Add,Alert,Copy,Delete,Exec,Get,Replace,Atomic,Sequence,";
40
41     private static final String JavaDoc ALERT_OPTION_MINDT = "MINDT" ;
42     private static final String JavaDoc ALERT_OPTION_MAXDT = "MAXDT" ;
43     private static final String JavaDoc ALERT_OPTION_DR = "DR" ;
44     private static final String JavaDoc ALERT_OPTION_MAXLEN = "MAXLEN";
45     private static final String JavaDoc ALERT_OPTION_IT = "IT" ;
46     private static final String JavaDoc ALERT_OPTION_ET = "ET" ;
47
48
49     // --------------------------------------------------- Public static methods
50

51     /**
52      * Converts the given array of ManagementOperations in AbstractCommands.
53      *
54      * @param commands the ManagementOperations to convert
55      * @param idGenerator generator for command ids
56      *
57      * @return the corresponding AbstractCommand array.
58      */

59     public static AbstractCommand[]
60         managementOperations2commands(ManagementOperation[] operations ,
61                                       CommandIdGenerator idGenerator) {
62
63             if ((operations == null) || (operations.length == 0)) {
64                 return new AbstractCommand[0];
65             }
66
67             AbstractCommand[] ret = new AbstractCommand[operations.length];
68             for (int i=0; i<operations.length; ++i) {
69                 ret[i] = managementOperation2command(
70                             operations[i], idGenerator.next(), idGenerator
71                          );
72             }
73
74             return ret;
75     }
76
77     /**
78      * Converts the given array of ManagementOperations in AbstractCommands.
79      *
80      * @param commands the ManagementOperations to convert
81      * @param cmdId id for the new command
82      *
83      * @return the corresponding AbstractCommand array.
84      */

85     public static AbstractCommand
86         managementOperation2command(ManagementOperation o, CmdID cmdId, CommandIdGenerator idGenerator) {
87
88         if (o instanceof AtomicManagementOperation) {
89             return new Atomic(
90                 cmdId,
91                 false, // noResp
92
null ,
93                 managementOperations2commands(((AtomicManagementOperation)o).getOperations(), idGenerator)
94                 );
95         } else if (o instanceof SequenceManagementOperation) {
96             return new Sequence(
97                 cmdId,
98                 false, // noResp
99
null ,
100                 managementOperations2commands(((SequenceManagementOperation)o).getOperations(), idGenerator)
101                 );
102         } else if (o instanceof AddManagementOperation) {
103             return new Add(
104                 cmdId,
105                 false, // noResp
106
null ,
107                 null ,
108                 nodes2Items(((AddManagementOperation)o).getNodes(), true)
109             );
110         } else if (o instanceof CopyManagementOperation) {
111             // @TODO - Copy management operation to a Sequence command
112
return null;
113         } else if (o instanceof DeleteManagementOperation) {
114             return new Delete(
115                 cmdId,
116                 false, // noResp
117
false, // archive
118
false, // soft delete
119
null ,
120                 null ,
121                 nodes2Items(((DeleteManagementOperation)o).getNodes(), false)
122             );
123         } else if (o instanceof ExecManagementOperation) {
124             return new Exec(
125                 cmdId,
126                 false, // noResp
127
null ,
128                 nodes2Items(((ExecManagementOperation)o).getNodes(), true)[0]
129             );
130         } else if (o instanceof GetManagementOperation) {
131             return new Get(
132                 cmdId,
133                 false, // noResp
134
null ,
135                 null ,
136                 null ,
137                 nodes2Items(((GetManagementOperation)o).getNodes(), false)
138             );
139         } else if (o instanceof ReplaceManagementOperation) {
140             return new Replace(
141                 cmdId,
142                 false, // noResp
143
null ,
144                 null ,
145                 nodes2Items(((ReplaceManagementOperation)o).getNodes(), true)
146             );
147         } else if (o instanceof UserAlertManagementOperation) {
148             UserAlertManagementOperation a = (UserAlertManagementOperation)o;
149
150             return new Alert(
151                 cmdId,
152                 false,
153                 null,
154                 a.getAlertCode(),
155                 alert2Items(a)
156             );
157         }
158
159         return null;
160     }
161
162     /**
163      * Converts a mangement nodes map into an array of Item objects.
164      * <p/>
165      *
166      * @param nodes the nodes map to convert
167      * @param useValue should values be set in Items?
168      *
169      * @return the Item[]
170      */

171     public static Item[] nodes2Items(Map JavaDoc nodes, boolean useValue) {
172         if ((nodes == null) || (nodes.size()==0)) {
173             return new Item[0];
174         }
175
176         Item[] items = new Item[nodes.size()];
177
178         Object JavaDoc key = null, value = null, treeNodeValue = null;
179
180         String JavaDoc format = null;
181         String JavaDoc type = null;
182         Meta meta = null;
183         ComplexData complexData = null;
184
185         int j = 0;
186         Iterator i = nodes.keySet().iterator();
187         TreeNode treeNode = null;
188         while(i.hasNext()) {
189             key = i.next();
190             value = nodes.get(key);
191             meta = null;
192             format = null;
193             type = null;
194             treeNodeValue = null;
195             treeNode = null;
196             complexData = null;
197
198             if (!(value instanceof TreeNode)) {
199                 // create a dummy TreeNode
200
value = new TreeNode((String JavaDoc)key, value);
201             }
202
203             treeNode = (TreeNode)value;
204
205             format = treeNode.getFormat();
206             key = treeNode.getName();
207             treeNodeValue = treeNode.getValue();
208             type = treeNode.getType();
209
210             if (format == null) {
211                 format = TreeNode.FORMAT_DEFAULT_VALUE;
212             }
213
214             if (format.equalsIgnoreCase(TreeNode.FORMAT_BINARY)) {
215                 if (treeNodeValue instanceof byte[]) {
216                     complexData = new ComplexData(new String JavaDoc(Base64.encode( (byte[])treeNodeValue)));
217                 } else {
218                     // get the byte[] of the string representation of the dataValue
219
complexData = new ComplexData(new String JavaDoc(Base64.encode(treeNodeValue.toString().
220                         getBytes())));
221                 }
222             } else if (format.equalsIgnoreCase(TreeNode.FORMAT_NODE)) {
223                 treeNodeValue = "";
224             }
225
226             if (useValue && (complexData == null)) {
227                 complexData = new ComplexData(String.valueOf(treeNodeValue));
228             }
229
230             meta = new Meta();
231             meta.setFormat(format);
232             meta.setType(type);
233
234             items[j++] = new Item(
235                             new Target(String.valueOf(key)),
236                             null ,
237                             (useValue) ? meta : null ,
238                             (useValue) ? complexData : null,
239                             false // moreData
240
);
241         }
242
243         return items;
244     }
245
246     /**
247      * Converts an array of Status and Results commands into an array of
248      * ManagementOperationResult objects.<br>
249      * Only the status to the following commands are taken into account:
250      *
251      * <ul>
252      * <li>Add</li>
253      * <li>Copy</li>
254      * <li>Delete</li>
255      * <li>Exec</li>
256      * <li>Get</li>
257      * <li>Replace</li>
258      * <li>Atomic</li>
259      * <li>Sequence</li>
260      * </ul>
261      *
262      * only if the their status code isn't contained in the given dischargedStatus.
263      * <br>The <code>dischargedStatus</code> must be in the following format:<br>
264      * <code>status1,status2,status3</code>. <br>This is used because for example the status
265      * code 213 isn't a result code (or final code. The processor not must be notified
266      * of this status).
267      *
268      * <p>
269      * Note that in the case of a Status for a Get command, as per the specs
270      * (OMA-SyncML-DMRepPro-V1_1_2-20030613-A) the following command is the
271      * Results. Its content will be stored into the property nodes.
272      * <p>
273      * In all other cases, nodes will contain an entry for each Status' Item
274      * (if there is any).
275      *
276      * @param commands the array of AbstractCommand to process
277      * @param dischargedStatus comma separated list of status code to ignore
278      * (see method description)
279      *
280      * @return the corresponding ManagementOperationResult[]
281      *
282      * @throws ProtocolException in case of something wrong with the protocol
283      */

284     public static ManagementOperationResult[]
285     operationResults(AbstractCommand[] commands, String JavaDoc dischargedStatus)
286     throws ProtocolException {
287         if ((commands == null) || (commands.length==0)) {
288             return new ManagementOperationResult[0];
289         }
290
291         TreeMap results = new TreeMap(new CmdIdComparator());
292
293         //
294
// We first pass throgh all statuses and fill the results map.
295
// Then we look for Results
296
//
297

298         //
299
// NOTE: accordingly with the specs "The optional MsgRef element type
300
// specifies the MsgID of the associated SyncML request. If the MsgRef
301
// is not present in a Results element type, then the MsgRef value of
302
// "1" MUST be assumed."
303
//
304

305         //
306
// 1. Status processing
307
//
308
String JavaDoc key = null;
309         Status status = null;
310         ManagementOperationResult s = null;
311         String JavaDoc statusCode = null;
312         for(int i=0; i<commands.length; ++i) {
313             if (!(commands[i] instanceof Status)) {
314                 continue;
315             }
316
317             status = (Status)commands[i];
318
319             if (MANAGEMENT_COMMANDS.indexOf(status.getCmd()) < 0) {
320                 continue;
321             }
322
323             statusCode = status.getData().getData();
324             if (dischargedStatus.indexOf(statusCode) != -1) {
325                 continue;
326             }
327
328             key = status.getMsgRef();
329             key = ((key == null) ? "1" : key)
330                 + '-'
331                 + status.getCmdRef()
332                 + '-'
333                 + status.getStatusCode()
334                 ;
335
336             s = (ManagementOperationResult)results.get(key);
337             if (s == null) {
338                 String JavaDoc cmd = status.getCmd();
339                 s = new ManagementOperationResult();
340                 s.setStatusCode(status.getStatusCode());
341                 s.setCommand(cmd);
342
343                 if (Alert.COMMAND_NAME.equals(cmd)) {
344                     s.setNodes(nodesFromItems(status));
345                 } else {
346                     s.setNodes(nodesFromTargetRefs(status));
347                 }
348
349                 results.put(key, s);
350             }
351         }
352
353         //
354
// 2. Results processing
355
//
356
Results res = null;
357         for(int i=0; i<commands.length; ++i) {
358             if (!(commands[i] instanceof Results)) {
359                 continue;
360             }
361
362             res = (Results)commands[i];
363
364             key = res.getMsgRef();
365             key = ((key == null) ? "1" : key)
366                 + '-'
367                 + res.getCmdRef()
368                 + "-200" // we suppose there was a 200 status, otherwise no
369
// Results should be here
370
;
371
372             s = (ManagementOperationResult)results.get(key);
373
374             if (s == null) {
375                 //
376
// This should not happen!
377
//
378
throw new ProtocolException( "Results "
379                                            + key
380                                            + " is without corresponding Status"
381                                            );
382             }
383
384             s.addNodes(nodesFromItems(res));
385         }
386
387         //
388
// Converts results into a ManagementOperationResult[]
389
//
390
ManagementOperationResult[] ret
391             = new ManagementOperationResult[results.size()];
392
393         int i = 0;
394         Iterator iter = results.values().iterator();
395         while (iter.hasNext()) {
396             ret[i++] = (ManagementOperationResult)iter.next();
397         }
398
399         return ret;
400     }
401
402     /**
403      * Returns a Map from the Items of a ItemizedCommand. If the given command
404      * is not a ItemizedCommand, an empty map is returned.
405      *
406      * @param cmd the status command
407      *
408      * @return a Map from the Items of a ItemizedCommand command
409      */

410     public static Map JavaDoc nodesFromItems(AbstractCommand cmd) {
411         HashMap ret = new HashMap();
412
413         if (!(cmd instanceof ItemizedCommand)) {
414             return ret;
415         }
416
417         String JavaDoc node = null;
418         Object JavaDoc value = null;
419
420         if (((ItemizedCommand)cmd).getItems() == null) {
421             return ret;
422         }
423
424         int c = 0;
425         Item item = null;
426         Iterator i = ((ItemizedCommand)cmd).getItems().iterator();
427         String JavaDoc format = null;
428         Data itemData = null;
429         while (i.hasNext()) {
430             item = (Item)i.next();
431             format = getItemFormat(item);
432
433             node = (item.getSource() == null)
434                  ? String.valueOf(++c)
435                  : item.getSource().getLocURI();
436
437             itemData = item.getData();
438             if (itemData != null) {
439                 value = itemData.getData();
440
441                 if (format.equalsIgnoreCase(TreeNode.FORMAT_DEFAULT_VALUE)) {
442                     value = String.valueOf(value);
443                 } else if (format.equalsIgnoreCase(TreeNode.FORMAT_BINARY)) {
444                     value = Base64.decode(String.valueOf(value).getBytes());
445                 } else if (format.equalsIgnoreCase(TreeNode.FORMAT_BOOL)) {
446                     value = new Boolean JavaDoc((String JavaDoc)value);
447                 } else if (format.equalsIgnoreCase(TreeNode.FORMAT_INT)) {
448                     try {
449                         value = new Integer JavaDoc( (String JavaDoc)value);
450                     } catch (NumberFormatException JavaDoc e) {
451                         // if the node not contains a valid integer, adds an exception
452
value = new ManagementException(
453                             "Node with format int not contains a valid integer value (" + value +
454                             ")");
455                     }
456                 }
457
458                 if (format.equalsIgnoreCase("node")) {
459                     value = new TreeNode(item.getSource().getLocURI(), value, format);
460                 }
461             }
462
463             ret.put(node, (value == null) ? "" : value);
464         }
465
466         return ret;
467     }
468
469     /**
470      * Returns a Map from the TargetRefs of a ResponseCommand. If the given
471      * command is not a ResponseCommand, an empty map is returned.
472      *
473      * @param cmd the status command
474      *
475      * @return a Map from the TargetRefs of a ResponseCommand command
476      */

477     public static Map JavaDoc nodesFromTargetRefs(AbstractCommand cmd) {
478         HashMap ret = new HashMap();
479
480         if (!(cmd instanceof ResponseCommand)) {
481             return ret;
482         }
483
484         String JavaDoc node = null;
485
486         if (((ResponseCommand)cmd).getTargetRef() == null) {
487             return ret;
488         }
489
490         TargetRef ref = null;
491         Iterator i = ((ResponseCommand)cmd).getTargetRef().iterator();
492         while (i.hasNext()) {
493             ref = (TargetRef)i.next();
494             node = ref.getValue();
495
496             ret.put(node, "");
497         }
498
499         return ret;
500     }
501
502     /**
503      * Returns an array of Item objects to be set into an Alert. The array is
504      * at least two length long: the first Item is the option string, the others
505      * are the real alerts.
506      *
507      * @param alert the UserAlertManagementObject instance
508      *
509      * @return the corresponding Item[] array
510      */

511     public static Item[] alert2Items(final UserAlertManagementOperation alert) {
512
513         String JavaDoc[] alerts = alert.getAlerts();
514
515         //
516
// We have to create one item for the options string, plus one item for
517
// each alert messages
518
//
519
Item[] items = new Item[(alerts == null) ? 1 : alerts.length+1];
520
521         //
522
// First of all, creates the option string.
523
//
524
StringBuffer JavaDoc options = new StringBuffer JavaDoc();
525
526         int i = alert.getMinDisplayTime();
527         if (i > 0) {
528             options.append(ALERT_OPTION_MINDT).append('=').append(i).append('&');
529         }
530         i = alert.getMaxDisplayTime();
531         if (i > 0) {
532             options.append(ALERT_OPTION_MAXDT).append('=').append(i).append('&');
533         }
534
535         String JavaDoc s = alert.getDefaultResponse();
536         if (s != null) {
537             try {
538                 options.append(ALERT_OPTION_DR).append('=').append(URLEncoder.encode(s, "UTF-8")).append('&');
539             } catch (java.io.UnsupportedEncodingException JavaDoc e) {
540                 options.append(e.getMessage());
541             }
542         }
543
544         i = alert.getMaxLength();
545         if (i > 0) {
546             options.append(ALERT_OPTION_MAXLEN).append('=').append(i).append('&');
547         }
548
549         char c = alert.getInputType();
550         if (c != ' ') {
551             options.append(ALERT_OPTION_IT).append('=').append(c).append('&');
552         }
553
554         c = alert.getEchoType();
555         if (c != ' ') {
556             options.append(ALERT_OPTION_ET).append('=').append(c).append('&');
557         }
558         if (options.length() > 0) {
559             options.deleteCharAt(options.length() - 1);
560         }
561
562         //
563
// Note the here alert options will always terminate with an additional
564
// '&' that must be removed.
565
//
566
items[0] = new Item(null, null, null, new ComplexData(options.toString()), false);
567
568         //
569
// Now we create a new Item for each alert message
570
//
571
for (i=1; i < items.length; ++i) {
572             items[i] = new Item(null, null, null, new ComplexData(alerts[i-1]), false);
573         }
574
575         return items;
576     }
577
578     // --------------------------------------------------------- Private methods
579

580     /**
581      * Returns the format of the item. If the format is not specified, returns
582      * the <code>ITEM_FORMAT_DEFAULT</code>
583      *
584      * @param item the item
585      * @return the format of the given item
586      */

587     private static String JavaDoc getItemFormat(Item item) {
588         Meta meta = item.getMeta();
589         if (meta == null) {
590             return TreeNode.FORMAT_DEFAULT_VALUE;
591         }
592         String JavaDoc format = meta.getFormat();
593         if (format == null) {
594             format = TreeNode.FORMAT_DEFAULT_VALUE;
595         }
596
597         return format;
598     }
599
600     // --------------------------------------------------------- Private classes
601

602     /**
603      * This class compares two strings in the form of keys used by the
604      * <i>operationResults</i> methods. They are formatted as follows:<br>
605      * <i>{msgref}-{cmdref}-{status}</i><br>
606      * The comparison must be done on the <i>{cmdref}</i> only, considering it a
607      * number.
608      */

609     private static class CmdIdComparator
610     implements Comparator {
611         /**
612          * @see java.util.Comparator
613          *
614          * @param o1 first argument
615          * @param o2 second argument
616          *
617          * @return a negative integer, zero, or a positive integer as the first
618          * argument is less than, equal to, or greater than the second.
619          */

620         public int compare(Object JavaDoc o1, Object JavaDoc o2) {
621             String JavaDoc value1 = null;
622             String JavaDoc value2 = null;
623
624             if (!((o1 instanceof String JavaDoc) && (o2 instanceof String JavaDoc))) {
625                 throw new IllegalArgumentException JavaDoc( "o1 ("
626                                                   + o1
627                                                   + ") and o2 ("
628                                                   + o2
629                                                   + ") must be string!"
630                                                   );
631             }
632
633             value1 = (String JavaDoc)o1;
634             value2 = (String JavaDoc)o2;
635
636             return extractCmdId(value1) - extractCmdId(value2);
637         }
638
639         /**
640          * Given a string in the form of <i>{msgref}-{cmdref}-{status}</i>,
641          * this method extracts the {cmdref} and returns it as an int.
642          * If the string is unparsable, IllegalArgumentException is thrown.
643          *
644          * @param s the string to be processed
645          *
646          * @return the cmdref as int
647          *
648          * @throws IllegalArgumentException if s is not in the form <i>{msgref}-{cmdref}-{status}</i>
649          */

650         private int extractCmdId(final String JavaDoc s)
651         throws IllegalArgumentException JavaDoc {
652             if (s == null) {
653                 throw new IllegalArgumentException JavaDoc("s cannot be null");
654             }
655
656             int p1 = s.indexOf('-');
657             if ((p1 <= 0) || (p1 == s.length()-1)) {
658                 throw new IllegalArgumentException JavaDoc("s is not in the form {msgref}-{cmdref}-{status}");
659             }
660
661             int p2 = s.indexOf('-', p1+1);
662
663             if (p2 <= 0) {
664                 throw new IllegalArgumentException JavaDoc("s is not in the form {msgref}-{cmdref}-{status}");
665             }
666
667             try {
668                 return Integer.parseInt(s.substring(p1+1, p2));
669             } catch (NumberFormatException JavaDoc e) {
670                 throw new IllegalArgumentException JavaDoc("s is not in the form {msgref}-{cmdref}-{status}");
671             }
672         }
673     }
674 }
Popular Tags