ViewUtility.java
001 /*
002  *
003  * Licensed to the Apache Software Foundation (ASF) under one
004  * or more contributor license agreements.  See the NOTICE file
005  * distributed with this work for additional information
006  * regarding copyright ownership.  The ASF licenses this file
007  * to you under the Apache License, Version 2.0 (the
008  * "License"); you may not use this file except in compliance
009  * with the License.  You may obtain a copy of the License at
010  
011  *   http://www.apache.org/licenses/LICENSE-2.0
012  
013  * Unless required by applicable law or agreed to in writing,
014  * software distributed under the License is distributed on an
015  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
016  * KIND, either express or implied.  See the License for the
017  * specific language governing permissions and limitations
018  * under the License.
019  *
020  */
021 package org.apache.qpid.management.ui.views;
022 
023 import java.io.UnsupportedEncodingException;
024 import java.nio.charset.Charset;
025 import java.security.MessageDigest;
026 import java.security.NoSuchAlgorithmException;
027 import java.util.ArrayList;
028 import java.util.Collections;
029 import java.util.Comparator;
030 import java.util.List;
031 import java.util.Map;
032 import java.util.Set;
033 
034 import javax.management.openmbean.ArrayType;
035 import javax.management.openmbean.CompositeData;
036 import javax.management.openmbean.CompositeDataSupport;
037 import javax.management.openmbean.CompositeType;
038 import javax.management.openmbean.OpenType;
039 import javax.management.openmbean.TabularDataSupport;
040 import javax.management.openmbean.TabularType;
041 
042 import org.apache.qpid.management.ui.ApplicationWorkbenchAdvisor;
043 import org.eclipse.core.runtime.IStatus;
044 import org.eclipse.core.runtime.Status;
045 import org.eclipse.jface.dialogs.ErrorDialog;
046 import org.eclipse.swt.SWT;
047 import org.eclipse.swt.events.SelectionAdapter;
048 import org.eclipse.swt.events.SelectionEvent;
049 import org.eclipse.swt.events.SelectionListener;
050 import org.eclipse.swt.layout.GridData;
051 import org.eclipse.swt.layout.GridLayout;
052 import org.eclipse.swt.widgets.Button;
053 import org.eclipse.swt.widgets.Composite;
054 import org.eclipse.swt.widgets.Control;
055 import org.eclipse.swt.widgets.Display;
056 import org.eclipse.swt.widgets.Label;
057 import org.eclipse.swt.widgets.MessageBox;
058 import org.eclipse.swt.widgets.Shell;
059 import org.eclipse.swt.widgets.Text;
060 import org.eclipse.ui.forms.widgets.FormToolkit;
061 
062 /**
063  * Utility Class for displaying OpenMbean data types by creating required SWT widgets
064  @author Bhupendra Bhardwaj
065  */
066 public class ViewUtility
067 {
068     public static final String OP_NAME     = "operation_name";
069     public static final String OP_PARAMS   = "parameters";
070     public static final String PARAMS_TEXT = "text";
071 
072     public static final String FIRST = "First";
073     public static final String LAST  = "Last";
074     public static final String NEXT  = "Next";
075     public static final String PREV  = "Previous";
076     public static final String INDEX = "Index";
077     
078     private static final Comparator tabularDataComparator = new TabularDataComparator();
079     
080     private static List<String> SUPPORTED_ARRAY_DATATYPES = new ArrayList<String>();
081     static
082     {
083         SUPPORTED_ARRAY_DATATYPES.add("java.lang.String");
084         SUPPORTED_ARRAY_DATATYPES.add("java.lang.Boolean");
085         SUPPORTED_ARRAY_DATATYPES.add("java.lang.Character");
086         SUPPORTED_ARRAY_DATATYPES.add("java.lang.Integer");
087         SUPPORTED_ARRAY_DATATYPES.add("java.lang.Long");
088         SUPPORTED_ARRAY_DATATYPES.add("java.lang.Double");
089         SUPPORTED_ARRAY_DATATYPES.add("java.util.Date");
090     }
091     
092     /**
093      * Populates the composite with given openmbean data type (TabularType or CompositeType)
094      @param toolkit
095      @param parent composite
096      @param data open mbean data type(either composite type or tabular data type)
097      */
098     public static void populateCompositeWithData(FormToolkit toolkit, Composite parent, Object data)
099     {
100         if (data instanceof TabularDataSupport)
101         {
102             ViewUtility.createTabularDataHolder(toolkit, parent, (TabularDataSupport)data);
103         }
104         else if (data instanceof CompositeDataSupport)
105         {
106             ViewUtility.populateCompositeWithCompositeData(toolkit, parent, (CompositeDataSupport)data);
107         }
108     }
109     
110     @SuppressWarnings("unchecked")
111     private static void createTabularDataHolder(FormToolkit toolkit, Composite parent, TabularDataSupport tabularData)
112     {
113         Composite composite = toolkit.createComposite(parent, SWT.BORDER);
114         GridLayout layout = new GridLayout(4true);
115         layout.horizontalSpacing = 0;
116         layout.marginWidth = 0;
117         layout.marginHeight = 10;
118         layout.verticalSpacing = 10;
119         composite.setLayout(layout);
120         composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
121 
122         Set entrySet = tabularData.entrySet();
123         ArrayList<Map.Entry> list = new ArrayList<Map.Entry>(entrySet);
124         if (list.size() == 0)
125         {
126             Text text = toolkit.createText(composite, " No records ", SWT.CENTER | SWT.SINGLE | SWT.READ_ONLY);
127             GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, true, 41);
128             text.setLayoutData(layoutData);
129             return;
130         }  
131         
132         Collections.sort(list, tabularDataComparator);
133      
134         // Attach the tabular record to be retrieved and shown later when record is traversed
135         // using first/next/previous/last buttons
136         composite.setData(list);
137         
138         // Create button and the composite for CompositeData
139         Composite compositeDataHolder = createCompositeDataHolder(toolkit, composite,
140                                         tabularData.getTabularType().getRowType());
141 
142         // display the first record
143         CompositeData data = (CompositeData)(list.get(0)).getValue();
144         composite.setData(INDEX, 0);
145         populateCompositeWithCompositeData(toolkit, compositeDataHolder, data);
146         enableOrDisableTraversalButtons(composite);
147     }
148 
149     private static void enableOrDisableTraversalButtons(Composite composite)
150     {
151         int index = (Integer)composite.getData(INDEX);
152         int size = ((List)composite.getData()).size();
153 
154         ((Button)composite.getData(FIRST)).setEnabled(true);
155         ((Button)composite.getData(PREV)).setEnabled(true);
156         ((Button)composite.getData(NEXT)).setEnabled(true);
157         ((Button)composite.getData(LAST)).setEnabled(true);
158 
159         if (index == 0)
160         {
161             ((Button)composite.getData(FIRST)).setEnabled(false);
162             ((Button)composite.getData(PREV)).setEnabled(false);
163         }
164         if (index == size -1)
165         {
166             ((Button)composite.getData(NEXT)).setEnabled(false);
167             ((Button)composite.getData(LAST)).setEnabled(false);
168         }
169     }
170 
171     /**
172      * Sets up the given composite for holding a CompositeData. Create traversal buttons, label etc and
173      * creates a child Composite, which should be populated with the CompositeData
174      @param toolkit
175      @param dataHolder
176      @param compositeType
177      @return
178      */
179     private static Composite createCompositeDataHolder(final FormToolkit toolkit, final Composite dataHolder, CompositeType compositeType)
180     {        
181         String desc = compositeType.getDescription();
182         Label description = toolkit.createLabel(dataHolder, desc, SWT.CENTER);
183         description.setLayoutData(new GridData(SWT.CENTER, SWT.TOP, true, false, 41));       
184         // TODO nameLabel.setFont(font);
185         description.setText(desc);
186 
187         // Add traversal buttons
188         final Button firstRecordButton = toolkit.createButton(dataHolder, FIRST, SWT.PUSH);
189         GridData layoutData = new GridData (GridData.HORIZONTAL_ALIGN_END);
190         layoutData.widthHint = 80;
191         firstRecordButton.setLayoutData(layoutData);
192 
193         final Button nextRecordButton = toolkit.createButton(dataHolder, NEXT, SWT.PUSH);
194         layoutData = new GridData (GridData.HORIZONTAL_ALIGN_END);
195         layoutData.widthHint = 80;
196         nextRecordButton.setLayoutData(layoutData);
197 
198         final Button previousRecordButton = toolkit.createButton(dataHolder, PREV, SWT.PUSH);
199         layoutData = new GridData (GridData.HORIZONTAL_ALIGN_BEGINNING);
200         layoutData.widthHint = 80;
201         previousRecordButton.setLayoutData(layoutData);
202 
203         final Button lastRecordButton = toolkit.createButton(dataHolder, LAST, SWT.PUSH);
204         layoutData = new GridData (GridData.HORIZONTAL_ALIGN_BEGINNING);
205         layoutData.widthHint = 80;
206         lastRecordButton.setLayoutData(layoutData);
207         
208         // Now create the composite, which will hold the CompositeData
209         final Composite composite = toolkit.createComposite(dataHolder, SWT.NONE);
210         GridLayout layout = new GridLayout();
211         layout.horizontalSpacing = layout.verticalSpacing = 0;
212         layout.marginHeight = layout.marginWidth = 0;
213         composite.setLayout(layout);
214         composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 41));
215 
216         // Add button references.  These references will be used when buttons
217         // are to enabled or disabled based of record index. e.g. for first record
218         // First and Previous buttons will be disabled.
219         dataHolder.setData(FIRST, firstRecordButton);
220         dataHolder.setData(NEXT, nextRecordButton);
221         dataHolder.setData(PREV, previousRecordButton);
222         dataHolder.setData(LAST, lastRecordButton);
223 
224         // Listener for the traversal buttons. When a button is clicked the respective
225         // CompositeData will be populated in the composite
226         SelectionListener listener = new SelectionAdapter()
227         {
228             public void widgetSelected(SelectionEvent e)
229             {
230                 if (!(e.widget instanceof Button))
231                     return;
232 
233                 Button traverseButton =(Button)e.widget; 
234                 // Get the CompositeData respective to the button selected
235                 CompositeData data = getCompositeData(dataHolder, traverseButton.getText());
236                 populateCompositeWithCompositeData(toolkit, composite, data);
237                 enableOrDisableTraversalButtons(dataHolder);   
238             }
239         };
240 
241         firstRecordButton.addSelectionListener(listener);    
242         nextRecordButton.addSelectionListener(listener);
243         previousRecordButton.addSelectionListener(listener);
244         lastRecordButton.addSelectionListener(listener);
245 
246         return composite;
247     }
248     
249     /**
250      * The CompositeData is set as data with the Composite and using the index, this method will
251      * return the corresponding CompositeData
252      @param compositeHolder
253      @param dataIndex
254      @return the CompositeData respective to the index
255      */
256     private static CompositeData getCompositeData(Composite compositeHolder, String dataIndex)
257     {
258         List objectData = (List)compositeHolder.getData();
259         if (objectData == null || objectData.isEmpty())
260         {
261             //          TODO
262         }
263 
264         // Get the index of record to be shown.
265         int index = 0;
266         if (compositeHolder.getData(INDEX!= null)
267         {
268             index = (Integer)compositeHolder.getData(INDEX);
269         }
270 
271         if (FIRST.equals(dataIndex))
272         {
273             index = 0;
274         }
275         else if (NEXT.equals(dataIndex))
276         {
277             index = index + 1;
278         }
279         else if (PREV.equals(dataIndex))
280         {
281             index = (index != 0(index = index - 1: index;
282         }
283         else if (LAST.equals(dataIndex))
284         {
285             index = objectData.size() -1;
286         }
287 
288         // Set the index being shown.
289         compositeHolder.setData(INDEX, index);
290 
291         return (CompositeData)((Map.Entry)objectData.get(index)).getValue();
292     }
293 
294     /**
295      * Populates the given composite with the CompositeData. Creates required widgets to hold the data types
296      @param toolkit
297      @param parent
298      @param data CompositeData
299      */
300     @SuppressWarnings("unchecked")
301     private static void populateCompositeWithCompositeData(FormToolkit toolkit, Composite parent, CompositeData data)
302     {
303         Control[] oldControls = parent.getChildren();       
304         for (int i = 0; i < oldControls.length; i++)
305         {
306             oldControls[i].dispose();
307         }
308         
309         Composite compositeHolder = toolkit.createComposite(parent, SWT.NONE);
310         GridLayout layout = new GridLayout(4false);
311         layout.horizontalSpacing = 10;
312         compositeHolder.setLayout(layout);
313         compositeHolder.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));       
314         
315         
316         // ItemNames in composite data
317         List<String> itemNames = new ArrayList<String>(data.getCompositeType().keySet());
318 
319         for (String itemName : itemNames)
320         {
321             OpenType itemType = data.getCompositeType().getType(itemName);
322             Label keyLabel = toolkit.createLabel(compositeHolder, itemName, SWT.TRAIL);
323             GridData layoutData = new GridData(SWT.FILL, SWT.FILL, false, false, 11);
324             layoutData.minimumWidth = 70;
325             keyLabel.setLayoutData(layoutData);
326 
327             if (itemType.isArray())
328             {
329                 OpenType type = ((ArrayType)itemType).getElementOpenType();
330                 //  If Byte array and mimetype is text, convert to text string
331                 if (type.getClassName().equals(Byte.class.getName()))
332                 {
333                     String mimeType = null
334                     String encoding = null;
335                     if (data.containsKey("MimeType"))
336                     {
337                         mimeType = (String)data.get("MimeType");
338                     }
339                     if (data.containsKey("Encoding"))
340                     {
341                         encoding = (String)data.get("Encoding");
342                     }
343                     
344                     if (encoding == null || encoding.length() == 0)
345                     {
346                         encoding = Charset.defaultCharset().name();
347                     }
348 
349                     if ("text/plain".equals(mimeType))
350                     {
351                         convertByteArray(toolkit, compositeHolder, data, itemName, encoding);
352                     }
353                     else
354                     {
355                         setNotSupportedDataType(toolkit, compositeHolder);
356                     }                        
357                 }
358                 // If array of any other supported type, show as a list of String array
359                 else if (SUPPORTED_ARRAY_DATATYPES.contains(type.getClassName()))
360                 {
361                     convertArrayItemForDisplay(compositeHolder, data, itemName);
362                 }
363                 else
364                 {
365                     setNotSupportedDataType(toolkit, compositeHolder);
366                 }
367             }
368             else if (itemType instanceof TabularType)
369             {
370                 Composite composite = toolkit.createComposite(compositeHolder, SWT.NONE);
371                 composite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 31));
372                 layout = new GridLayout();
373                 layout.marginHeight = 0;
374                 layout.marginWidth = 0;
375                 composite.setLayout(layout);
376                 createTabularDataHolder(toolkit, composite, (TabularDataSupport)data.get(itemName));
377             }
378             else
379             {
380                 Text valueText = toolkit.createText(compositeHolder, String.valueOf(data.get(itemName)), SWT.READ_ONLY | SWT.BORDER);
381                 valueText.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 31));
382             }
383         }   
384         
385         // layout the composite after creating new widgets.
386         parent.layout();
387     //end of method
388   
389     
390     private static void convertByteArray(FormToolkit toolkit, Composite compositeHolder, CompositeData data, String itemName, String encoding)
391     {
392         Byte[] arrayItems = (Byte[])data.get(itemName);
393         byte[] byteArray = new byte[arrayItems.length];
394 
395         for (int i = 0; i < arrayItems.length; i++)
396         {
397             byteArray[i= arrayItems[i];
398         }
399         try
400         {
401             String textMessage = new String(byteArray, encoding);
402 
403             Text valueText = toolkit.createText(compositeHolder, textMessage, SWT.READ_ONLY | SWT.BORDER |
404                     SWT.MULTI | SWT.WRAP | SWT.V_SCROLL);
405             GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true, 31);
406             gridData.heightHint = 300;
407             valueText.setLayoutData(gridData);
408         }
409         catch(Exception ex)
410         {
411             ex.printStackTrace();
412         }
413     }
414 
415     private static Shell getShell()
416     {
417         Shell shell = Display.getCurrent().getActiveShell();
418 
419         // Under linux GTK getActiveShell returns null so we need to make a new shell for display.
420         // Under windows this is fine.
421         if (shell == null)
422         {
423             // This occurs under linux gtk
424             shell = new Shell(Display.getCurrent(), SWT.BORDER | SWT.CLOSE | SWT.MIN | SWT.MAX);
425         }
426 
427         return shell;
428     }
429 
430     private static int showBox(String title, String message, int icon)
431     {
432         MessageBox messageBox = new MessageBox(getShell(), icon);
433         messageBox.setMessage(message);
434         messageBox.setText(title);
435 
436         return messageBox.open();
437     }
438 
439     public static int popupInfoMessage(String title, String message)
440     {
441         return showBox(title, message, SWT.ICON_INFORMATION | SWT.OK);
442     }
443     
444     public static int popupErrorMessage(String title, String message)
445     {
446         return showBox(title, message, SWT.ICON_ERROR | SWT.OK);
447     }
448 
449     public static int popupConfirmationMessage(String title, String message)
450     {
451         return showBox(title, message,SWT.ICON_QUESTION | SWT.YES | SWT.NO | SWT.CANCEL);
452     }
453 
454     
455     public static Shell createPopupShell(String title, int width, int height)
456     {
457         Display display = Display.getCurrent();
458         Shell shell = new Shell(display, SWT.BORDER | SWT.CLOSE | SWT.MIN |SWT.MAX);
459         shell.setText(title);
460         shell.setLayout(new GridLayout());       
461         int x = display.getBounds().width;
462         int y = display.getBounds().height;
463         shell.setBounds(x/4, y/4, width, height)
464         
465         return shell;
466     }
467     
468     /**
469      * Creates a List widget for displaying array of strings
470      @param compositeHolder
471      @param data - containing the array item value
472      @param itemName - item name
473      */
474     private static void convertArrayItemForDisplay(Composite compositeHolder, CompositeData data, String itemName)
475     {
476         Object[] arrayItems = (Object[])data.get(itemName);
477         String[] items = new String[arrayItems.length];
478         for (int i = 0; i < arrayItems.length; i++)
479         {
480             items[i= String.valueOf(arrayItems[i]);
481         }
482         org.eclipse.swt.widgets.List list = new org.eclipse.swt.widgets.List(compositeHolder,
483                 SWT.MULTI | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL | SWT.READ_ONLY);
484         list.setItems(items);
485         //list.setBackground(compositeHolder.getBackground());
486         list.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 31));
487     }
488     
489     private static void setNotSupportedDataType(FormToolkit toolkit, Composite compositeHolder)
490     {
491         Text valueText = toolkit.createText(compositeHolder, "--- Content can not be displayed ---", SWT.READ_ONLY);
492         valueText.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 31));
493     }
494     
495     /**
496      * Converts the input string to displayable format by converting some character case or inserting space
497      @param input
498      @return formatted string
499      */
500     public static String getDisplayText(String input)
501     {
502         StringBuffer result = new StringBuffer(input);
503         if (Character.isLowerCase(result.charAt(0)))
504         {
505             result.setCharAt(0, Character.toUpperCase(result.charAt(0)));
506         }
507         for (int i = 1; i < input.length(); i++)
508         {
509             if (Character.isUpperCase(result.charAt(i)) && !Character.isWhitespace(result.charAt(i - 1))
510                                                         && Character.isLowerCase(result.charAt(i - 1)))
511             {
512                 result.insert(i, " ");
513                 i++;
514             }
515             else if (Character.isLowerCase(result.charAt(i)) && Character.isWhitespace(result.charAt(i - 1)))
516             {
517                 result.setCharAt(i, Character.toUpperCase(result.charAt(i)));
518             }
519                 
520         }
521         
522         return result.toString();
523     }
524     
525     /**
526      * Disposes the children of given Composite if not null and not already disposed
527      @param parent composite
528      */
529     public static void disposeChildren(Composite parent)
530     {
531         if (parent == null || parent.isDisposed())
532             return;
533         
534         Control[] oldControls = parent.getChildren();        
535         for (int i = 0; i < oldControls.length; i++)
536         {
537             oldControls[i].dispose();
538         }
539     }       
540     
541     public static char[] getHash(String textthrows NoSuchAlgorithmException, UnsupportedEncodingException
542     {
543         byte[] data = text.getBytes("utf-8");
544 
545         MessageDigest md = MessageDigest.getInstance("MD5");
546 
547         for (byte b : data)
548         {
549             md.update(b);
550         }
551 
552         byte[] digest = md.digest();
553 
554         char[] hash = new char[digest.length ];
555 
556         int index = 0;
557         for (byte b : digest)
558         {            
559             hash[index++(charb;
560         }
561 
562         return hash;
563     }
564     
565     private static class TabularDataComparator implements java.util.Comparator<Map.Entry>
566     {
567         public int compare(Map.Entry data1, Map.Entry data2)
568         {
569             if (data1.getKey() instanceof List)
570             {
571                 Object obj1 = ((List)data1.getKey()).get(0);                
572                 Object obj2 = ((List)data2.getKey()).get(0);
573                 String str1 = obj1.toString();
574                 String str2 = obj2.toString();
575                 if (obj1 instanceof String)
576                 {
577                     return str1.compareTo(str2);
578                 }
579                 
580                 try
581                 {
582                     return Long.valueOf(str1).compareTo(Long.valueOf(str2));
583                 }
584                 catch (Exception ex)
585                 {
586                     return -1;
587                 }
588             }
589            
590             return -1;
591         }
592     }
593 }