|
||
![]() |
|
![]() |
|
Table of Contents
Voici une liste de notes techniques qui peuvent s’avérer utiles pour répondre à un problème donné. Atelier Moni - Trucs et astuces
Eclipse Buddy Class LoadingPrincipesChaque plugin a son propre Class Loader. Celui-ci peut accéder aux classes du plugin et à celles des packages exportés explicitement dans les plugins dont il dépend. ProblèmeSi un plugin doit charger une classe quelconque, d’un plugin dont il ne dépend pas, ça ne fonctionne pas. Ce cas survient pourtant de temps en temps. Exemple : un plugin encapsulant Log4j veut accéder à une classe d’un plugin P1 qui utilise Log4j, mais la dépendance est déclarée de P1 vers Log4j et pas dans l’autre sens. Déclarer la dépendance dans l’autre sens n’aurait pas de sens justement, car il n’est pas raisonnable d’ajouter au plugin Log4j tous les plugins susceptibles de l’utiliser... Celui-ci doit rester indépendant et ne doit pas être modifié sous prétexte qu’un nouveau plugin l’utilise. La solutionLa solution s’appelle “Buddy class loading”. Il suffit de déclarer ce mécanisme dans les fichiers MANIFEST.MF :
Eclipse-BuddyPolicy: registered
Eclipse-RegisterBuddy: <nom du plugin partagé> C’est une manière implicite de déclarer la relation dans l’autre sens. PrinciplesEach plugin has its own Class Loader. This one can reach all the plugin classes and those from the packages explicitly exported in the plugins on which it depends. ProblemIf a plugin must load som class from a plugin on which it does not depend, you will get an exception. However it could be useful in some cases. Example: a plugin encapsulating Log4j wants to access a class from a P1 plugin which uses Log4j, but dependence is declared from P1 towards Log4j and not in the other way. Declaring the dependence in the other way would not have sense, because adding to Log4j a dependance towards every plugin would not be satisfying... Indeed, this one must remain independent and does not have to be modified just because a new plugin needs to use it. SolutionThe solution is called “Buddy class loading”. You have to declare it in Manifest.MF files :
Eclipse-BuddyPolicy: registered
Eclipse-RegisterBuddy: <name of shared plugin> Thus you implicitly declare a sort of “dependency” in the other way round. Bundle of JRE with Eclipse RCPIf you put the bundled jre into the jre sub-directory of your rcp app, Eclipse will guarantee to use this without extra options. Eclipse RCP and OneNoteEclipse is able to talk with OneNote... Demonstration
SnippetHere is a snippet to connect SWT and OneNote : import java.io.IOException; import org.eclipse.swt.SWT; import org.eclipse.swt.dnd.Clipboard; import org.eclipse.swt.dnd.HTMLTransfer; import org.eclipse.swt.dnd.RTFTransfer; import org.eclipse.swt.dnd.TextTransfer; import org.eclipse.swt.dnd.Transfer; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.layout.RowData; import org.eclipse.swt.layout.RowLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; public class OneNoteCopyPaste { private static final String PASTE_CMD = " /paste"; private static final String ONENOTE_PATH = "C:/Program Files/Microsoft Office/OFFICE11/onenote.exe"; public static void main (String [] args) { final Display display = new Display (); Shell shell = new Shell (display); Label label = new Label (shell, SWT.NONE); label.setText ("Enter your name:"); final Text text = new Text (shell, SWT.BORDER); text.setLayoutData (new RowData (100, SWT.DEFAULT)); Button ok = new Button (shell, SWT.PUSH); ok.setText ("OK"); ok.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { Clipboard cb = new Clipboard(display); String textData = text.getText(); String rtfData = "{\\rtf1\\b\\i " + textData + "}"; String htmlData = "<html><body>" + textData+ "</body></html>"; TextTransfer textTransfer = TextTransfer.getInstance(); RTFTransfer rtfTransfer = RTFTransfer.getInstance(); HTMLTransfer htmlTransfer = HTMLTransfer.getInstance(); Transfer[] transfers = new Transfer[]{textTransfer, rtfTransfer, htmlTransfer}; Object[] data = new Object[]{textData, rtfData, htmlData}; cb.setContents(data, transfers); try { Runtime.getRuntime().exec(ONENOTE_PATH + PASTE_CMD); } catch (IOException ioe) { ioe.printStackTrace(); } finally { cb.dispose(); } } }); Button cancel = new Button (shell, SWT.PUSH); cancel.setText ("Cancel"); cancel.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { System.out.println("Cancel"); } }); shell.setDefaultButton (cancel); shell.setLayout (new RowLayout ()); shell.pack (); shell.open (); while (!shell.isDisposed ()) { if (!display.readAndDispatch ()) display.sleep (); } display.dispose (); } } Checkboxes dans une table SWTComment mettre des checkboxes dans une table SWT ? Plusieurs solutions se présentent... En standardLa table SWT n’accepte les checkboxes que dans sa première colonne. Pour activer ce comportement, on utilise le style SWT.CHECK. new Table(parent, SWT.CHECK); Cette checkbox a cependant un comportement particulier et l’action de la cocher n’est qu’une alternative à la sélection de ligne. En conséquence, c’est un SelectionListener qui pourra intercepter les changements d’état : table.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { if (e.detail == SWT.CHECK) { ... } } }); En résumé, cette façon de procéder est assez simple à mettre en oeuvre mais ne permet pas d’avoir des cases à cocher dans plusieurs colonnes distinctes qui, par exemple, correspondraient à des booléens sur des beans. Avec JFaceOn peut simuler la présence de checkboxes dans un TableViewer. Pour cela, on implémente ICellModifier (voir cet article sur l'utilisation de TableViewer) et on fera en sorte que le LabelProvider retourne une image qui corresponde selon les cas à la case cochée ou décochée. Cette approche comporte quelques limitations :
TableEditorUtiliser TableEditor permet d’ajouter une véritable checkbox (bouton de style check) aux cellules de son choix. TableEditor editor = new TableEditor(table); editor.grabVertical = false; editor.minimumHeight = 18; Button button = new Button(table, SWT.CHECK); button.pack(); editor.horizontalAlignment = SWT.CENTER; editor.minimumWidth = button.getSize().x; editor.setEditor(button, item, colIndex); où item est le TableItem qui correspond à la ligne et colIndex est l’indice de la colonne. Pour traiter les changements d’état : button.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { Button checkBox = (Button) e.getSource(); boolean selected = checkBox.getSelection(); ... } }); On peut procéder de la même manière pour mettre en oeuvre des boutons radio. Utiliser JasperReports avec Eclipse RCP
L’utilisation de JasperReports dans une application RCP provoque des Dans une application Eclipse RCP chaque plugin possède son propre classloader. Par défaut ce classloader ne donne accès qu’aux classes dudit plugin et aux classes exportées par les plugins dont il dépend. Pour fonctionner, JasperReports génère dynamiquement une classe représentant le Report. Puisque cette classe n’est pas connue du plugin, elle n’est pas chargée. Eclipse propose un mécanisme nommé “buddy loading” mais celui-ci n’est pas utilisable dans le cas de JasperReports. La solution consiste à définir un nouveau classloader. Créer la classe JasperContextFinder
Notre classloader va être créé en dupliquant la classe ClassLoader basicFindClassLoader() { Class[] stack = contextFinder.getClassContext(); for (int i = 1; i < stack.length; i++) { ClassLoader tmp = stack[i].getClassLoader(); if (stack[i] != ContextFinder.class && tmp != null && checkClassLoader(tmp)) { return tmp; } } return null; } Utiliser JasperReportsIl ne reste plus qu’a utiliser JasperReports avec notre classloader. // Taking backup of the default classloader ClassLoader systemClassLoader = Thread.currentThread().getContextClassLoader(); try { // Loading my custom classloader Thread.currentThread().setContextClassLoader( new JasperContextFinder(Thread.currentThread() .getContextClassLoader())); // Manipulation de Jasper ... JasperPrint jasperPrint = JasperFillManager.fillReport(...); ... } finally { // Replacing my custom classloader with the original classloader. Thread.currentThread().setContextClassLoader(systemClassLoader); } SafeRunnableLa plate-forme Eclipse apporte un mécanisme pour gérer les erreurs qui surviennent sur un traitement. Définition du mécanismeISafeRunnable (org.eclipse.core.runtime) permet de définir une portion de code protégée. L’interface se compose de 2 méthodes :
D’autre part, ISafeRunnableRunner définit un cadre pour l’exécution de telles portions de code via une méthode run() qui prend en paramètre une implémentation d’ISafeRunnable. L'implémentation SafeRunnableSafeRunnable (org.eclipse.jface.util) est une mise en oeuvre de ce mécanisme. En effet, cette classe propose à la fois :
SafeRunnable.run(new SafeRunnable() { public void run() throws Exception { ... <traitement> ... } }); Ce genre de code peut donc remplacer un bloc try/catch lorsque l’on souhaite généraliser la gestion des erreurs. Il est évidemment possible de définir un autre traitement d’erreurs que celui par défaut. Des onglets avec le look Eclipse 3Par défaut les applications Eclipse RCP montrent des onglets rectangulaires plutôt classiques. Pour avoir des onglets qui ressemblent à ceux d’Eclipse IDE version 3.x..., ...il suffit d’ajouter la ligne de code suivante : PlatformUI.getPreferenceStore().setValue( IWorkbenchPreferenceConstants.SHOW_TRADITIONAL_STYLE_TABS, false); Ce code peut être placé dans ApplicationWorkbenchAdvisor.initialize(). Il permet simplement de passer de ce genre d’onglet : à celui-ci : Ce n’est sans doute pas une fonctionnalité indispensable mais un bon moyen de donner du caractère à l’IHM à peu de frais. Intégrer des JARs à une application RCPPour intégrer des JARs à une application Eclipse RCP, il faut...
NB : Il n’est pas utile d’ajouter les JARs au classpath du projet via son menu Properties. Si on le fait une erreur de redondance sera signalée. Si les fonctionnalités apportées par les bibliothèques sont susceptibles d’être utilisées assez souvent, il est conseillé de créer un plugin plutôt que d’intégrer simplement les JARs à l’application. Eclipse propose un assistant pour créer des plugins à partir de JARs existants. ⇒ on bénéficie ainsi de la souplesse et de la puissance de la notion de plugin dans Eclipse (versionning, features, update...)
Manipulation d'images avec RCPQuelques bouts de code pour manipuler les images avec Eclipse RCP, en particulier accéder aux icônes de la plate-forme. Récupérer une icône de la plate-formePlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_FOLDER); ISharedImages (org.eclipse.ui) porte les identifiants des icônes apportées en standard par la plate-forme (images partagées). Remarques :
Ajouter sa propre image aux images partagéesOn déclare l’image au niveau de ApplicationWorkbenchAdvisor.initialize(IWorkbenchConfigurer) de la façon suivante : URL link = Platform.getBundle("my plugin").getEntry("icons/myicon.gif"); ImageDescriptor icon = ImageDescriptor.createFromURL(link); configurer.declareImage(MY_ICON_KEY, icon, true); “my plugin” est le Bundle_SymbolicName du plugin déclaré dans MANIFEST.MF Accéder à une vue RCPComment accéder à une vue RCP depuis n’importe quel endroit du code ? IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); IViewPart view = page.findView(MyView.ID); Pour afficher directement la vue : page.showView(MyView.ID); |
||