/*
 * Copyright
 *   2005 axYus - www.axyus.com
 *   2005 C.Marchand - christophe.marchand@axyus.com
 *
 * This file is part of XEMELIOS.
 *
 * XEMELIOS is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * XEMELIOS is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with XEMELIOS; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package fr.gouv.finances.cp.xemelios.ui;

import fr.gouv.finances.cp.xemelios.updater.UpdaterUtil;
import fr.gouv.finances.dgfip.xemelios.common.config.Loader;
import fr.gouv.finances.cp.xemelios.controls.extend.MainControlWrapper;
import fr.gouv.finances.dgfip.xemelios.common.components.XemeliosComponentsModel;
import fr.gouv.finances.dgfip.xemelios.common.components.XemeliosComponentsParser;
import fr.gouv.finances.cp.xemelios.starter.Bootstrap;
import fr.gouv.finances.dgfip.xemelios.importers.XemeliosArchiveImporter.Errors;
import fr.gouv.finances.dgfip.xemelios.tools.AbstractTool;
import fr.gouv.finances.dgfip.xemelios.tools.ToolConstants;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.DisplayMode;
import java.awt.Frame;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Box;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JDesktopPane;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;
import javax.swing.border.EtchedBorder;
import javax.swing.filechooser.FileFilter;
import javax.swing.filechooser.FileSystemView;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.apache.log4j.Logger;
import org.apache.log4j.xml.DOMConfigurator;
import org.jdesktop.swingworker.SwingWorker;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import fr.gouv.finances.dgfip.utils.NavigationContext;
import fr.gouv.finances.dgfip.utils.Pair;
import fr.gouv.finances.cp.utils.PropertiesExpansion;
import fr.gouv.finances.cp.utils.StartUrl;
import fr.gouv.finances.cp.utils.ui.DisplayExceptionDlg;
import fr.gouv.finances.cp.utils.ui.IhmFactory;
import fr.gouv.finances.cp.utils.ui.JWaiter;
import fr.gouv.finances.cp.utils.ui.RamViewer;
import fr.gouv.finances.cp.utils.xml.marshal.XmlOutputter;
import fr.gouv.finances.dgfip.utils.xml.xpath.FunctionResolver;
import fr.gouv.finances.cp.xemelios.Splash;
import fr.gouv.finances.cp.xemelios.auth.LoginDialog;
import fr.gouv.finances.dgfip.xemelios.auth.XemeliosUser;
import fr.gouv.finances.dgfip.xemelios.common.Constants;
import fr.gouv.finances.dgfip.xemelios.common.DematTransform;
import fr.gouv.finances.dgfip.xemelios.common.PJRef;
import fr.gouv.finances.dgfip.xemelios.common.ToolException;
import fr.gouv.finances.dgfip.xemelios.common.config.DocumentModel;
import fr.gouv.finances.dgfip.xemelios.common.config.DocumentsModel;
import fr.gouv.finances.dgfip.xemelios.common.config.ElementModel;
import fr.gouv.finances.dgfip.xemelios.common.config.EnvironmentDomain;
import fr.gouv.finances.dgfip.xemelios.common.config.EtatModel;
import fr.gouv.finances.dgfip.xemelios.common.config.PersonnalConfigParser;
import fr.gouv.finances.dgfip.xemelios.common.config.PersonnalConfigurationModel;
import fr.gouv.finances.dgfip.xemelios.common.config.SavedRequestsModel;
import fr.gouv.finances.dgfip.xemelios.common.listeners.StartListener;
import fr.gouv.finances.cp.xemelios.controls.DlgControl;
import fr.gouv.finances.cp.xemelios.controls.MainControl;
import fr.gouv.finances.dgfip.xemelios.data.DataAccessException;
import fr.gouv.finances.dgfip.xemelios.data.DataConfigurationException;
import fr.gouv.finances.dgfip.xemelios.data.DataImpl;
import fr.gouv.finances.dgfip.xemelios.data.DataLayerManager;
import fr.gouv.finances.dgfip.xemelios.importers.EtatImporteur;
import fr.gouv.finances.cp.xemelios.importers.ImportServiceSwingProvider;
import fr.gouv.finances.cp.xemelios.ui.admin.DlgSystemConfiguration;
import fr.gouv.finances.cp.xemelios.ui.collectivites.DlgSelectNavigationTarget;
import fr.gouv.finances.cp.xemelios.ui.export.DlgSearchConfig;
import fr.gouv.finances.cp.xemelios.ui.importDialog.ImportDialog;
import fr.gouv.finances.cp.xemelios.ui.importDialog.ImportFile;
import fr.gouv.finances.cp.xemelios.updater.AbstractConfigurator;
import fr.gouv.finances.cp.xemelios.updater.script.config.ComponentModel;
import fr.gouv.finances.cp.xemelios.updater.script.config.ComponentsParser;
import fr.gouv.finances.cp.xemelios.updater.script.config.XemeliosComponentModel;
import fr.gouv.finances.cp.xemelios.updater.ui.DlgUpdate;
import fr.gouv.finances.dgfip.utils.ClassPathUtils;
import fr.gouv.finances.dgfip.utils.IoUtils;
import fr.gouv.finances.dgfip.xemelios.auth.AuthEngine;
import fr.gouv.finances.dgfip.xemelios.auth.UnauthorizedException;
import fr.gouv.finances.dgfip.xemelios.data.impl.pool.Pool;
import fr.gouv.finances.dgfip.xemelios.data.impl.pool.PoolManager;
import fr.gouv.finances.dgfip.xemelios.importers.XemeliosArchiveImporter;
import fr.gouv.finances.dgfip.xemelios.tools.MenuTitle;
import fr.gouv.finances.dgfip.xemelios.ui.ImageResources;
import fr.gouv.finances.dgfip.xemelios.utils.FileUtils;
import java.io.FilenameFilter;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.zip.GZIPInputStream;
import javax.swing.Timer;

/**
 * Main Xemelios window
 *
 * @author christophe.marchand Date: 15 aot 2004 Time: 11:46:18
 */
public class MainWindow  extends JFrame {
    
    private static Logger logger = null;
    /**
     * Comment for <code>serialVersionUID</code>
     */
    private static final long serialVersionUID = 3977304325938558517L;
    public static final transient String MAIN_WINDOW_POSITION = "main.window.last.location";
    
    public static final int DEFAULT_WIDTH = 800;
    public static final int DEFAULT_HEIGHT = 600;
    private Action actExporter, actExporterCsv, actQuitter;
    private JDesktopPane desk;
    private JTextField statusField;
    private StartListener startListener;
    private DocumentsModel docModels;
    protected File lastInputDirectory = null;
    private JMenu searchMenu;
    private JMenu windowMenu;
    private JMenu browseMenu;
    private JMenu exportMenu;
    private JMenu importerMenu=null;
    private JMenu controlerMenu=null;
    private JMenuItem mnuClean=null;
    private JMenuItem mnuCleanPro=null;
    private JLabel backImage;
    private JWaiter waiter;
    private PersonnalConfigurationModel config;
    private XemeliosUser connectedUser = null;
    // Ne pas renommer, utilise sans lien dans AbstractTool
    public static MainWindow instance  = null;
    public XPathFactory xpf;
    private Properties criteriasInputValues;
    private String componentsModelFileName,updateUrl;
    
    private PropertiesExpansion pe;
//	int grantAccess=-1;
    /**
     * Separateur par defaut pour le CSV
     */
    public static String fieldSeparator = ";";
    
    public MainWindow(String title, StartListener sl, String[] args) {
        super(title);
        if(instance!=null) throw new RuntimeException("Only one instance of MainWindow can exists");
        instance = this;
        this.startListener = sl;
        startListener.pushProgress();
        try {
            xpf = XPathFactory.newInstance(XPathFactory.DEFAULT_OBJECT_MODEL_URI);
        } catch(Throwable t) {
            xpf = XPathFactory.newInstance();
        }
        xpf.setXPathFunctionResolver(new FunctionResolver());
        try {
            loadConfig();
            checkUpdateScript();
//            applyPatches();
            initComponents();
            try {
                if(!checkPassword()) {
                    startListener.completeProgress();
                    runExit();
                }
            } catch(Throwable t) {
                new DisplayExceptionDlg(this,t);
                runExit();
            }
            initMenus2();
            setMenusAccess();
            if(!"true".equals(System.getProperty(Constants.SYS_PROP_SKIP_UPDATE_CHECK))) checkUpdateAvailability();
            startListener.completeProgress();
            if(args.length>0) {
                starterImport(args);
            }
        } catch(DataConfigurationException dcEx) {
            Throwable ex = dcEx;
            while(ex.getCause()!=null) ex=ex.getCause();
            ex.printStackTrace();
        }
    }
    /**
     * Cette methode charge la configuration depuis le fichier de conf
     * @throws DataConfigurationException
     */
    protected void loadConfig() throws DataConfigurationException {
        if(System.getProperty(Constants.SYS_PROP_LOG4J)!=null) {
            String log4jConfFile = System.getProperty(Constants.SYS_PROP_LOG4J);
            DOMConfigurator.configure(log4jConfFile);
            logger = Logger.getLogger(this.getClass());
            logger.info("log4J initialise");
        }
        pe = new PropertiesExpansion();
        for(Object key:System.getProperties().keySet()) {
            pe.setProperty((String)key,System.getProperty((String)key));
        }
        componentsModelFileName=pe.getProperty(Constants.SYS_PROP_COMPONENTS_FILENAME);
        updateUrl=pe.getProperty(Constants.SYSPROP_UPDATE_SOURCE);
        docModels = getDocumentsInfos(pe.getProperty(Constants.SYS_PROP_DOC_DEF_DIR));
        initDataLayer();
        applyPatches();
logger.debug("Starting auth engine");
        AuthEngine.Holder.init();
logger.debug("Loading personnal config");
        loadPersonnalConfig();
    }
    
    protected void initComponents() {
        criteriasInputValues = loadCriteriasInputValues();
        IhmFactory iF = IhmFactory.newInstance();
        initActions();
        startListener.pushProgress();
        desk = new JDesktopPane();
        desk.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
        ImageIcon image = IhmFactory.getIconFromResource(ImageResources.SPLASH);
        backImage = new JLabel(image);
        backImage.setBounds(0, 0, image.getIconWidth(), image.getIconHeight());
        desk.add(backImage, JLayeredPane.FRAME_CONTENT_LAYER);
        initMenus1();
        startListener.pushProgress();
        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosed(WindowEvent e) {
            }
            
            @Override
            public void windowClosing(WindowEvent e) {
                runExit();
                logger.debug("closing");
            }
        });
        desk.addComponentListener(new ComponentAdapter() {
            @Override
            public void componentResized(ComponentEvent evt) {
                Dimension dim = evt.getComponent().getSize();
                Dimension imgSize = backImage.getSize();
                backImage.setLocation((int) (dim.getWidth() - imgSize
                        .getWidth()) / 2, (int) (dim.getHeight() - imgSize
                        .getHeight()) / 2);
            }
        });
        startListener.pushProgress();
        JPanel inner = new JPanel(new BorderLayout());
        JPanel status = new JPanel(new BorderLayout());
        startListener.pushProgress();
        status.add(statusField = new JTextField(30), BorderLayout.WEST);
        startListener.pushProgress();
        statusField.setEditable(false);
        statusField.setBorder(new EmptyBorder(0, 0, 0, 0));
        startListener.pushProgress();
        status.add(waiter = new JWaiter(), BorderLayout.EAST);
        if("true".equals(System.getProperty(Constants.SYS_PROP_ADMIN_MENU_GC))) {
            status.add(new RamViewer(),BorderLayout.CENTER);
        }
        if("true".equals(System.getProperty(Constants.SYS_PROP_SHOW_POOL_STATUS))) {
            status.add(new PoolStatDisplayer());
        }
        status.setBorder(new EtchedBorder(EtchedBorder.LOWERED));
        inner.add(status, BorderLayout.SOUTH);
        startListener.pushProgress();
        inner.add(desk, BorderLayout.CENTER);
        setContentPane(inner);
        startListener.pushProgress();
        
        // dimensionnement de la fenetre
        File f = new File(System.getProperty("user.home"), "xemelios.properties");
        Properties props = new Properties();
        if (f.exists()) {
            try {
                props.load(new FileInputStream(f));
            } catch (IOException ioEx) {
            }
        }
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        GraphicsConfiguration gc = getGraphicsConfiguration();
        GraphicsDevice gd = gc.getDevice();
        DisplayMode dm = gd.getDisplayMode();
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        Rectangle rect = ge.getMaximumWindowBounds();

        Rectangle lastLocation = null;
        if(props.getProperty(MAIN_WINDOW_POSITION)!=null) {
            // syntax is "(x,y,width,height)"
            String s = props.getProperty(MAIN_WINDOW_POSITION);
            s = s.substring(1,s.length()-1);
            String[] sa = s.split(",");
            try {
                int x = Integer.parseInt(sa[0]);
                int y = Integer.parseInt(sa[1]);
                int width = Integer.parseInt(sa[2]);
                int height = Integer.parseInt(sa[3]);
                lastLocation = new Rectangle(x,y,width,height);
                // si le rectangle enregistré est plus grand que l'ecran, on ne restaure pas la position
                if(lastLocation.getWidth()>rect.getWidth() || lastLocation.getHeight()>rect.getHeight()) lastLocation = null;
            } catch(Exception ex) {
                // on ignre, on initialise pas
            }
        }
        if(lastLocation!=null) {
            setBounds(lastLocation);
        } else {

            if(rect!=null && (rect.getWidth()!=dm.getWidth() || rect.getHeight()!=dm.getHeight())) {
                setBounds(rect);
            } else {
                setLocation((int) ((screenSize.getWidth() - DEFAULT_WIDTH) / 2),
                        (int) ((screenSize.getHeight() - DEFAULT_HEIGHT) / 2));
                pack();
                setSize(new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT));
                setExtendedState(MAXIMIZED_BOTH);
            }
        }
        
        startListener.pushProgress();
        setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        startListener.pushProgress();
        setVisible(true);
        startListener.pushProgress();
    }
    
    protected void initActions() {
        startListener.pushProgress();
        actExporter = new AbstractAction("Export avancé...") {
            /**
             * Comment for <code>serialVersionUID</code>
             */
            private static final long serialVersionUID = 1L;
            
            public void actionPerformed(ActionEvent e) {
                exporter();
            }
        };
        actExporter.setEnabled(false);
        actExporterCsv = new AbstractAction("Export...") {
            /**
             * Comment for <code>serialVersionUID</code>
             */
            private static final long serialVersionUID = 1L;
            
            public void actionPerformed(ActionEvent e) {
                exporterCSV();
            }
        };
        actExporter.setEnabled(false);
        startListener.pushProgress();
        actQuitter = new AbstractAction("Quitter") {
            /**
             * Comment for <code>serialVersionUID</code>
             */
            private static final long serialVersionUID = 1L;
            
            public void actionPerformed(ActionEvent e) {
                runExit();
            }
        };
        startListener.pushProgress();
    }
    protected void initMenus1() {
        JMenuBar mBar = new JMenuBar();
        JMenu fileMenu = new JMenu("Fichier");
        
        importerMenu = new JMenu("Importer");
        controlerMenu = new JMenu("Analyser");
        fileMenu.add(importerMenu);
        fileMenu.add(controlerMenu);
        fileMenu.addSeparator();
        fileMenu.add(new JMenuItem(actExporterCsv));
        fileMenu.add(new JMenuItem(actExporter));
        fileMenu.addSeparator();
        if ("true".equals(System.getProperty(Constants.SYS_PROP_RESET_CONFIG))) {
            JMenuItem mnuReset = new JMenuItem("Reset config ...");
            fileMenu.add(mnuReset);
            mnuReset.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent evt) {
                    reloadConfiguration();
                }
            });
        }
        if("true".equals(System.getProperty(Constants.SYS_PROP_ADMIN_MENU_GC))) {
            mnuClean = new JMenuItem("Garbage collector");
            fileMenu.add(mnuClean);
            mnuClean.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent evt) {
                    System.gc();
                }
            });
            mnuClean.setEnabled(false);
        }
        mnuCleanPro = new JMenuItem("Supprimer...");
        fileMenu.add(mnuCleanPro);
        mnuCleanPro.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                advancedCleanUp();
            }
        });
        fileMenu.addSeparator();
        JMenuItem mnuEnv;
        fileMenu.add(mnuEnv = new JMenuItem("Environnement..."));
        mnuEnv.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                DlgEditEnvironment dee = new DlgEditEnvironment(MainWindow.this,docModels,EnvironmentDomain.DOMAIN_DOCUMENTS);
                dee.setVisible(true);
            }
        });
        fileMenu.addSeparator();
        JMenuItem mnuUpdate = new JMenuItem("Mises à jour...");
        mnuUpdate.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                new DlgUpdate(MainWindow.this);
            }
        });
        fileMenu.add(mnuUpdate);
        fileMenu.addSeparator();
        
        JMenuItem mnuConfig = new JMenuItem("Configuration...");
        mnuConfig.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                DlgSystemConfiguration dlg = new DlgSystemConfiguration(MainWindow.this,true);
                dlg.setVisible(true);
            }
        });
        fileMenu.add(mnuConfig);
        fileMenu.addSeparator();
        
        fileMenu.add(new JMenuItem(actQuitter));
        mBar.add(fileMenu);
        
        searchMenu = new JMenu("Recherche");
        browseMenu = new JMenu("Navigation");
        
        searchMenu.setEnabled(false);
        browseMenu.setEnabled(false);
        mBar.add(searchMenu);
        mBar.add(browseMenu);
        
        // Export XLS
//        if(getConnectedUser().hasRole(XemeliosUser.ROLE_EXPORT)) {
            exportMenu = new JMenu("Exports");
            JMenuItem exportMenuConfig = new JMenuItem("Configurer");
            exportMenuConfig.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent evt) {
                    configExport();
                }
            });
            exportMenu.add(exportMenuConfig);
            JMenuItem exportMenuLancer = new JMenuItem("Exécuter");
            exportMenuLancer.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent evt) {
                    launchExport();
                }
            });
            exportMenu.add(exportMenuLancer);
            mBar.add(exportMenu);
//        }
        
        JMenu toolMenu = loadToolMenu();
        if(toolMenu!=null)
            mBar.add(toolMenu);
        
        windowMenu = new WindowMenu("Fenêtres",desk);
        
        mBar.add(windowMenu);
        
        mBar.add(Box.createHorizontalGlue());
        
        JMenu helper = new JMenu("?");
        Action aboutAction = new AbstractAction("A Propos") {
            private static final long serialVersionUID = 1L;
            
            public void actionPerformed(ActionEvent evt) {
                new AboutBox(MainWindow.this);
            }
        };
        helper.add(new JMenuItem(aboutAction));
        Action helpAction = new AbstractAction("Aide en ligne") {
            /**
             * Comment for <code>serialVersionUID</code>
             */
            private static final long serialVersionUID = 1L;
            
            public void actionPerformed(ActionEvent evt) {
                try {
                    StartUrl.startUrl(System.getProperty(Constants.SYS_PROP_HELP_URL));
                } catch(Throwable t) {}
            }
        };
        helper.add(helpAction);
        Action dlInfos = new AbstractAction("Rapport de configuration...") {
            private static final long serialVersionUID = 1L;
            public void actionPerformed(ActionEvent e) {
                try {
                    MainWindow.getInstance().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
                    StringBuilder sb = new StringBuilder();
                    sb.append("Jar Files\n");
                    sb.append(ClassPathUtils.displayClassPathInfos());
                    sb.append("xemelios.properties\n-------------------\n");
                    try {
                        File f = new File(getEnvProperties().replace("${xemelios.prg}/root/xemelios.properties"));
                        sb.append(FileUtils.readTextFile(f, "ISO-8859-1")).append("\n");
                    } catch(Exception ex) {
                        sb.append(" -- not readable : ").append(ex.getMessage()).append("\n");
                    }

                    sb.append("mysql.properties\n----------------\n");
                    try {
                        File f = new File(getEnvProperties().replace("${xemelios.mysql.config.filename}"));
                        sb.append(FileUtils.readTextFile(f, "ISO-8859-1")).append("\n");
                    } catch(Exception ex) {
                        sb.append(" -- not readable : ").append(ex.getMessage()).append("\n");
                        logger.error("reading mysql.properties",ex);
                    }

                    sb.append(DataLayerManager.getImplementation().getDatalayerConfiguration());

                    sb.append("xemelios.log\n----------------\n");
                    try {
                        File f = new File(getEnvProperties().replace("${xemelios.prg}/root/xemelios.log"));
                        sb.append(FileUtils.readTextFile(f, "ISO-8859-1")).append("\n");
                    } catch(Exception ex) {
                        sb.append(" -- not readable : ").append(ex.getMessage()).append("\n");
                        logger.error("reading xemelios.log",ex);
                    }


                    JFileChooser fc = new JFileChooser();
                    fc.setDialogTitle("Enregistrer le rapport de configuration sous...");
                    fc.addChoosableFileFilter(new FileFilter() {
                        @Override
                        public boolean accept(File f) {
                            return f.getName().toLowerCase().endsWith(".txt");
                        }
                        @Override
                        public String getDescription() {
                            return "Fichier texte (.txt)";
                        }
                    });
                    MainWindow.getInstance().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
                    if(fc.showSaveDialog(MainWindow.getInstance())==JFileChooser.APPROVE_OPTION) {
                        File f = fc.getSelectedFile();
                        PrintWriter pw = new PrintWriter(f);
                        pw.write(sb.toString());
                        pw.flush(); pw.close();
                    }
//                    logger.info(sb.toString());
                } catch(Throwable t) {
                } finally {
                    MainWindow.getInstance().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
                }
            }
        };
        helper.add(dlInfos);
        mBar.add(helper);
        
        setJMenuBar(mBar);
    }
    protected void initMenus2() {
        // Ajout des items du menu importer
        ActionImport aiArch = new ActionImport("Archive Xemelios",Constants.XEMELIOS_ARCHIVE_SIGN);
        importerMenu.add(new JMenuItem(aiArch));
        for(DocumentModel dm:docModels.getDocuments()) {
            if(!getConnectedUser().hasDocument(dm.getId())) {
                logger.debug(getConnectedUser().getId()+" is not allowed on "+dm.getId());
                continue;
            }
            if(dm.getDisplayInMenuIf()!=null && !"true".equals(System.getProperty(dm.getDisplayInMenuIf()))) continue;
            ActionImport ai = new ActionImport(dm.getTitre(),dm.getId());
            if(dm.isControlable()){
                ActionControl ac = new ActionControl(dm.getTitre(),dm.getId());
                controlerMenu.add(new JMenuItem(ac));
            }
            importerMenu.add(new JMenuItem(ai));
            JMenu searchEntry = new JMenu(dm.getTitre());
            JMenuItem browseEntry = null;
            if(dm.isBrowsable()) {
                final DocumentModel dm2 = dm;
                //browseEntry = new BrowseMenu(dm);
                browseEntry = new JMenuItem(new AbstractAction(dm.getTitre()) {
                    private static final long serialVersionUID = 1L;
                    public void actionPerformed(ActionEvent e) {
                        DlgSelectNavigationTarget dlg = new DlgSelectNavigationTarget(dm2, getConnectedUser(), instance);
                        if(dlg.run()) {
                            switch(dlg.getSpecialKeys().size()) {
                                case 0: {
                                    browse(dm2, dm2.getDefaultBrowsableEtat(), dlg.getCollectivite().key, dlg.getBudget().key,null,null);
                                    break;
                                }
                                case 1: {
                                    browse(dm2, dm2.getDefaultBrowsableEtat(), dlg.getCollectivite().key, dlg.getBudget().key, dlg.getSpecialKeys().get(0).key,null);
                                    break;
                                }
                                default: browse(dm2, dm2.getDefaultBrowsableEtat(), dlg.getCollectivite().key, dlg.getBudget().key, dlg.getSpecialKeys().get(0).key, dlg.getSpecialKeys().get(1).key);
                            }
                        }
                    }
                });
            }
            int searchCount=0;
            for(Enumeration<EtatModel> enumer2=dm.getEtats().elements();enumer2.hasMoreElements();) {
                EtatModel em = enumer2.nextElement();
                if(em.getDisplayInMenuIf()!=null && !"true".equals(System.getProperty(em.getDisplayInMenuIf()))) continue;
                JMenu tmpMnu = null;
                if(!dm.isGroupMenuItem()) {
                    tmpMnu = new JMenu(em.getTitre());
                } else {
                    tmpMnu = searchEntry;
                }
                for(ElementModel elm:em.getSearchableElements()) {
                    if(!elm.isDisplay()) continue;
                    ActionSearch as = new ActionSearch(elm.getTitre(),dm.getId(),em.getId(),elm.getId());
                    tmpMnu.add(new JMenuItem(as));
                    searchCount++;
                }
                if(tmpMnu!=searchEntry) {
                    if(tmpMnu.getMenuComponentCount()>0) searchEntry.add(tmpMnu);
                }
            }
            if(searchCount>0) {
                searchMenu.add(searchEntry);
            }
            if(browseEntry!=null) {
                browseMenu.add(browseEntry);
            }
        }
    }
    
    protected void importer(String docId) {
        if(Constants.XEMELIOS_ARCHIVE_SIGN.equals(docId)) {
            importXemeliosArchive();
        } else {
            DocumentModel dm = docModels.getDocumentById(docId);
            try {
                Class clazz = Class.forName(dm.getImportClass());
                Constructor cc = clazz.getConstructor(XemeliosUser.class);
                Object obj = cc.newInstance(getConnectedUser());
                if(!(obj instanceof EtatImporteur)) {
                    JOptionPane.showMessageDialog(this,"Cette classe n'est pas un importeur.\nLe fichier de configuration qui vous a été livré est certainement invalide.\nVeuillez contacter votre fournisseur.\n","Erreur",JOptionPane.ERROR_MESSAGE);
                    return;
                }
                EtatImporteur ei = (EtatImporteur)obj;
                ImportContent fToImport = getFilesToImport(dm.getExtension(),dm.getLibelleExtension());
                doImport(ei,fToImport,dm, false);
            } catch(Exception ex) {
                logger.error("importer",ex);
                new DisplayExceptionDlg(this,ex);
            } finally {
                //waiter.stopProgress();
            }
        }
    }
    protected void importXemeliosArchive() {
        FileSystemView fsv = FileSystemView.getFileSystemView();
        if (lastInputDirectory == null)
            lastInputDirectory = fsv.getHomeDirectory();
        JFileChooser fc = new JFileChooser(lastInputDirectory, fsv);
        FileFilter defaultFileFilter = null;
        fc.addChoosableFileFilter(defaultFileFilter = new FileFilter() {
            @Override
            public boolean accept(File arg0) {
                return arg0.isDirectory()
                || arg0.getName().toUpperCase().endsWith(".XAR");
            }

            @Override
            public String getDescription() {
                return "Archives Xemelios (.xar)";
            }
        });
        fc.setFileFilter(defaultFileFilter);
        fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
        fc.setMultiSelectionEnabled(false);
        fc.setDialogTitle("Choisissez le premier fichier d'une archive Xemelios");
        fc.showOpenDialog(this);
        File firstArchive = fc.getSelectedFile();
        lastInputDirectory = firstArchive.getParentFile();
        ImportServiceSwingProvider isp = new ImportServiceSwingProvider(this, null, 0);
        isp.setDisplayFeedback(false);
        XemeliosArchiveImporter xai = new XemeliosArchiveImporter(docModels, firstArchive, isp);
        XemeliosArchiveImporterWorker worker = new XemeliosArchiveImporterWorker(xai);
        worker.execute();
    }
    public void doImport(EtatImporteur ei,ImportContent fToImport,DocumentModel dm, boolean synchronizeMe){
        ImportServiceSwingProvider issp = new ImportServiceSwingProvider(this,ei,fToImport.filesToImport.size()+fToImport.pjs.size());
        if(synchronizeMe) issp.setDisplayFeedback(false);
        ei.setImpSvcProvider(issp);
        ei.setDocument(dm);
        waiter.startProgress();
        try {
            ei.setFilesToImport(fToImport.filesToImport);
            ei.setPjs(fToImport.pjs);
            if(synchronizeMe) {
                ei.run();
            } else {
                ei.execute();
            }
        } catch (Exception ex) {
            logger.error("doImport",ex);
            new DisplayExceptionDlg(this,ex);
        } finally {
            waiter.stopProgress();
        }
    }
    public void doImport(EtatImporteur ei,ImportContent fToImport,DocumentModel dm, DlgControl parentController, MainControl.Triplet triplet,boolean synchronizeMe) {
        ImportServiceSwingProvider issp = new ImportServiceSwingProvider(this,ei,fToImport.filesToImport.size()+fToImport.pjs.size());
        if(synchronizeMe) issp.setDisplayFeedback(false);
        ei.setImpSvcProvider(issp);
        ei.setDocument(dm);
        waiter.startProgress();
        try {
            ei.setFilesToImport(fToImport.filesToImport);
            ei.setPjs(fToImport.pjs);
            if(synchronizeMe) {
                ei.run();
            } else {
                ei.execute();
            }
            
        } catch (Exception ex) {
            logger.error("doImport",ex);
            new DisplayExceptionDlg(this,ex);
        } finally {
            waiter.stopProgress();
        }
        try {
            ei.get();
        } catch(Throwable t) {
            t.printStackTrace();
            parentController.notifyImportFailed();
        } finally {
            parentController.notifyImportFinished(triplet);
        }
    }
    protected void controller(String docId){
        MainControlWrapper mc = new MainControlWrapper(docModels.getDocumentById(docId),docModels,getConnectedUser());
        DlgControl dc = new DlgControl(this,docModels.getDocumentById(docId),mc);
		  dc.setVisible(true);
    }
    protected void exporter() {
        JInternalFrame f = desk.getSelectedFrame();
        if(f!=null) {
            SearchWindow s = (SearchWindow)f;
            if(s.canExport()) {
                s.exportXLS();
            }
        }
    }
    protected void exporterCSV() {
        JInternalFrame f = desk.getSelectedFrame();
        if(f!=null) {
            SearchWindow s = (SearchWindow)f;
            if(s.canExport()) {
                s.exportCSV();
            }
        }
    }
    protected void runExit() {
        this.setCursor(new Cursor(Cursor.WAIT_CURSOR));
        JInternalFrame[] childs = desk.getAllFrames();
        for(int i=0;i<childs.length;i++) {
            desk.remove(childs[i]);
            childs[i].dispose();
        }
        Rectangle location = getBounds();
        StringBuilder propValue = new StringBuilder();
        propValue.append("(").append(Integer.toString((int)location.getX())).append(",").append(Integer.toString((int)location.getY())).append(",").append(Integer.toString((int)location.getWidth())).append(",").append(Integer.toString((int)location.getHeight())).append(")");
        File propFile = new File(System.getProperty("user.home"), "xemelios.properties");
        Properties props = new Properties();
        if (propFile.exists()) {
            try {
                props.load(new FileInputStream(propFile));
            } catch (IOException ioEx) {
            }
        }
        props.setProperty(MAIN_WINDOW_POSITION, propValue.toString());
        try {
            props.store(new FileOutputStream(propFile), "");
        } catch(Throwable t) {}
        try {
            File tmpDir = new File(System.getProperty("java.io.tmpdir"));
            tmpDir = new File(tmpDir,Constants.NOM_APP);
            File[] files = tmpDir.listFiles();
            for(File f:files) f.delete();
            tmpDir.delete();
        } catch(Throwable t) {}
//        Thread[] thList = new Thread[100];
//        Thread.currentThread().getThreadGroup().enumerate(thList,true);
//        for(int i=0;i<thList.length;i++) {
//            if(thList[i]==null) break;
//        }
        savePersonnalConfig();
        saveCriteriasInputValues();
        System.exit(0);
    }
    public SearchWindow displaySearchWindow(ElementModel element) {
        ElementModel elm = element.clone();
        EtatModel em = element.getParent();
        DocumentModel dm = em.getParent();
        elm.setParent(em);
        try {
            if(DataLayerManager.getImplementation().canSearch(dm,em,getConnectedUser())) {
                if(elm==null) {
                    JOptionPane.showMessageDialog(this,"L'état "+em.getTitre()+" n'a pas d'élément de recherche définit.","Erreur de configuration",JOptionPane.ERROR_MESSAGE);
                    return null;
                }
                SearchWindow sw = new SearchWindow(this,dm,elm,getConnectedUser());
                desk.add(sw);
                desk.getDesktopManager().maximizeFrame(sw);
                return sw;
            } else {
                throw new ToolException(ToolException.ERROR_NO_DATA);
            }
        } catch(ToolException tEx) {
            if(tEx.getErrorCode()==ToolException.ERROR_NO_DATA) {
                JOptionPane.showMessageDialog(this,"Aucun "+em.getTitre()+" n'a été importé.\nVous devez en importer au moins un avant d'effectuer une recherche.","Erreur",JOptionPane.WARNING_MESSAGE);
            } else {
                new DisplayExceptionDlg(this,tEx);
            }
        } catch(UnauthorizedException ex) {
            JOptionPane.showMessageDialog(this, ex.getMessage(), "Droits insuffisants", JOptionPane.WARNING_MESSAGE);
        } catch(DataAccessException daEx) {
            new DisplayExceptionDlg(this,daEx);
        } catch(DataConfigurationException dcEx) {
            new DisplayExceptionDlg(this,dcEx);
        }
        return null;
    }
    protected void search(String docId,String etatId,String elementId) {
        DocumentModel dm = docModels.getDocumentById(docId).clone();
        EtatModel em = dm.getEtatById(etatId);
        try {
            if(DataLayerManager.getImplementation().canSearch(dm,em,getConnectedUser())) {
                ElementModel elm = em.getElementById(elementId);
                if(elm==null) {
                    JOptionPane.showMessageDialog(this,"L'état "+em.getTitre()+" n'a pas d'élément de recherche définit.","Erreur de configuration",JOptionPane.ERROR_MESSAGE);
                    return;
                }
                SearchWindow sw = new SearchWindow(this,dm,elm,getConnectedUser());
                desk.add(sw);
                desk.getDesktopManager().maximizeFrame(sw);
                sw.show();
            } else {
                throw new ToolException(ToolException.ERROR_NO_DATA);
            }
        } catch(ToolException tEx) {
            if(tEx.getErrorCode()==ToolException.ERROR_NO_DATA) {
                JOptionPane.showMessageDialog(this,"Aucun "+em.getTitre()+" n'a été importé.\nVous devez en importer au moins un avant d'effectuer une recherche.","Erreur",JOptionPane.WARNING_MESSAGE);
            } else {
                new DisplayExceptionDlg(this,tEx);
            }
        } catch(UnauthorizedException ex) {
            JOptionPane.showMessageDialog(this, ex.getMessage(), "Droits insuffisants", JOptionPane.WARNING_MESSAGE);
        } catch(DataAccessException daEx) {
            new DisplayExceptionDlg(this,daEx);
        } catch(DataConfigurationException dcEx) {
            new DisplayExceptionDlg(this,dcEx);
        }
    }

    protected boolean checkPassword() throws DataAccessException {
        // Affichage de la fenêtre d'authentification :
        AuthEngine authEngine = AuthEngine.Holder.getAuthEngine();
        XemeliosUser user = null;
        if (authEngine.isLoginRequired() || authEngine.isPasswordRequired()) {
            if (startListener instanceof Splash) {
                Splash sp = (Splash) startListener;
                sp.setAlwaysOnTop(false);
                sp.setVisible(false);
            }
            int attempt = 0;
            while (user == null && attempt < 3) {
                attempt++;
                LoginDialog loginDialog = new LoginDialog(this, authEngine.isLoginRequired(), authEngine.isPasswordRequired());
                if (loginDialog.run()) {
                    user = authEngine.createUser(loginDialog.getUserName(), loginDialog.getPassword());
                    if (user == null) {
                        JOptionPane.showMessageDialog(this, "Echec de l'authentification.", "Erreur", JOptionPane.ERROR_MESSAGE);
                    }
                } else {
                    if (startListener instanceof Splash) {
                        Splash sp = (Splash) startListener;
                        sp.setAlwaysOnTop(true);
                        sp.setVisible(true);
                    }
                    return false;
                }
            }
            if (startListener instanceof Splash) {
                Splash sp = (Splash) startListener;
                sp.setAlwaysOnTop(true);
                sp.setVisible(true);
            }
            if(user!=null) {

            }
        } else {
            // Nous sommes dans le cas ou le moteur d'authentification n'a ni besoin du login ni du mot de passe.
            // On n'affiche donc pas le fenêtre de login.
            user = authEngine.createUser(null, null);
        }
        if (user != null) {
            connectedUser = user;
            return true;
        } else {
            return false;
        }
    }
    
    protected DocumentsModel getDocumentsInfos(String repertoires) {
        DocumentsModel ret = null;
        try {
            ret = Loader.getDocumentsInfos(repertoires);
        } catch (Exception ex) {
            logger.error( ex);
            startListener.completeProgress();
            if (startListener instanceof Splash) {
                Splash sp = (Splash) startListener;
                sp.setAlwaysOnTop(false);
                sp.setVisible(false);
            }
            new DisplayExceptionDlg((JFrame) null, ex);
        }
        return ret;
    }
    
    protected ImportContent getFilesToImport(final String extension, final String titreEtat) {
        ImportContent ic = new ImportContent();
        FileSystemView fsv = FileSystemView.getFileSystemView();
        if (lastInputDirectory == null)
            lastInputDirectory = fsv.getHomeDirectory();
        JFileChooser fc = new JFileChooser(lastInputDirectory, fsv);
        FileFilter defaultFileFilter = null;
        fc.addChoosableFileFilter(defaultFileFilter = new FileFilter() {
            @Override
            public boolean accept(File arg0) {
                return arg0.isDirectory()
                || arg0.getName().toUpperCase().endsWith(
                        extension.toUpperCase());
            }
            
            @Override
            public String getDescription() {
                return "Fichiers " + titreEtat;
            }
        });
        fc.addChoosableFileFilter(new FileFilter() {
            @Override
            public boolean accept(File arg0) {
                return arg0.isDirectory()
                || arg0.getName().toUpperCase().endsWith(".XML");
            }
            
            @Override
            public String getDescription() {
                return "Fichiers XML";
            }
        });
        fc.addChoosableFileFilter(new FileFilter() {
            @Override
            public boolean accept(File arg0) {
                return arg0.isDirectory()
                || arg0.getName().toUpperCase().endsWith(".ZIP");
            }
            
            @Override
            public String getDescription() {
                return "Archives ZIP";
            }
        });
        fc.addChoosableFileFilter(new FileFilter() {
            @Override
            public boolean accept(File f) {
                return f.isDirectory() || f.getName().toUpperCase().endsWith(".GZ");
            }
            @Override
            public String getDescription() {
                return "Fichier compressé (GZip)";
            }
            
        });
        fc.setFileFilter(defaultFileFilter);
        fc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
        fc.setMultiSelectionEnabled(true);
        fc.setDialogTitle("Choisissez un fichier ou répertoire à importer");
        fc.showOpenDialog(this);
        File[] ret = null;
        if(fc.getSelectedFile()!=null && fc.getSelectedFile().isFile()) {
            // si l'utilisateur a choisi un fichier
//			ret = new File[1];
//			ret[0] = fc.getSelectedFile();
            ret = fc.getSelectedFiles();
            lastInputDirectory = ret[0].getParentFile();
        } else if(fc.getSelectedFile()!=null && fc.getSelectedFile().isDirectory()) {
            // si l'utilisateur a choisi un r?ertoire
            logger.debug("path="+fc.getSelectedFile().getAbsolutePath());
            // on cr? le mod?e de donn?s pour l'affichage dans la boite de dialogue d'import
            String[] extensions = {"xml", "zip", extension};
            
            // on affiche les fichiers trouvés dans les répertoires à l'utilisateur, pour
            // qu'il décide ceux qu'il veut exclure de l'import
            ImportDialog test = new ImportDialog(this, "Sélectionnez les fichiers à importer");
            List<ImportFile> resultats = test.startDialog(fc.getSelectedFile(), extensions);
            ret = new File[resultats.size()];
            int counter = 0;
            for(Iterator iter = resultats.iterator(); iter.hasNext(); ) {
                ret[counter++] = ((ImportFile)iter.next()).getFile();
            }
            lastInputDirectory = fc.getSelectedFile();
        } else {
            // l'utilisateur n'a rien choisi, appuy?sur "Annuler"
            ret = new File[0];
        }
        
        // on regarde si l'un des fichiers a importer est un zip
        //ArrayList<File> files = new ArrayList<File>();
        File tmpDir = new File(System.getProperty("java.io.tmpdir"));
        for (int i = 0; i < ret.length; i++) {
            if (ret[i].getName().toLowerCase().endsWith(".zip")) {
                if (ret[i].exists()) {
                    ZipFile zf = null;
                    try {
                        zf = new ZipFile(ret[i]);
                        for (Enumeration<? extends ZipEntry> enumer = zf
                                .entries(); enumer.hasMoreElements();) {
                            ZipEntry ze = enumer.nextElement();
                            if (!ze.isDirectory()) {
                                String fileName = ze.getName();
                                String entryName = fileName.toLowerCase();
                                fileName = fileName.replace(
                                        File.pathSeparatorChar, '_').replace(
                                        File.separatorChar, '_').replace(':',
                                        '|').replace('\'', '_').replace('/',
                                        '_');
                                logger.debug(entryName);
                                if(PJRef.isPJ(ze)) {
                                    PJRef pj = new PJRef(ze);
                                    pj.writeTmpFile(tmpDir,zf);
                                    ic.pjs.add(pj);
                                } else if ((entryName.endsWith(extension.toLowerCase()) || entryName.endsWith(".xml")) && !fileName.startsWith("_")) {
                                    // on decompresse le fichier dans le
                                    // repertoire temporaire, comme ca il sera
                                    // supprime en quittant
                                    InputStream is = zf.getInputStream(ze);
                                    BufferedInputStream bis = new BufferedInputStream(is);
                                    File output = new File(tmpDir, fileName);
                                    BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(output));
                                    byte[] buffer = new byte[1024];
                                    int read = bis.read(buffer);
                                    while (read > 0) {
                                        bos.write(buffer, 0, read);
                                        read = bis.read(buffer);
                                    }
                                    bos.flush();
                                    bos.close();
                                    bis.close();
                                    ic.filesToImport.add(output);
                                }
                            }
                        }
                        zf.close();
                    } catch (ZipException zEx) {
                        JOptionPane.showMessageDialog(this, "Le fichier "
                                + ret[i].getName()
                                + " n'est pas une archive ZIP valide",
                                "Erreur", JOptionPane.ERROR_MESSAGE);
                    } catch (IOException ioEx) {
                        JOptionPane
                                .showMessageDialog(
                                this,
                                "Le fichier "
                                + ret[i].getName()
                                + " est illisible. Vérifiez que les noms des fichiers\ncontenus dans l'archive ne comportent pas de caractères spéciaux ou accentués.",
                                "Erreur", JOptionPane.ERROR_MESSAGE);
                        ioEx.printStackTrace();
                    } finally {
                        if(zf!=null) {
                            try { zf.close(); } catch(Throwable t) {}
                        }
                    }
                }
            } else if(ret[i].getName().toLowerCase().endsWith(".gz")) {
                try {
                    String fileName = ret[i].getName();
                    fileName = fileName.substring(0, fileName.length()-3);
                    File output = new File(tmpDir, fileName);
                    GZIPInputStream gis = new GZIPInputStream(new FileInputStream(ret[i]));
                    BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(output));
                    byte[] buffer = new byte[1024];
                    int read = gis.read(buffer);
                    while (read > 0) {
                        bos.write(buffer, 0, read);
                        read = gis.read(buffer);
                    }
                    bos.flush();
                    bos.close();
                    gis.close();
                    ic.filesToImport.add(output);
                } catch(IOException ioEx) {
                    // nothing to do
                }
            } else {
                ic.filesToImport.add(ret[i]);
            }
        }
        return ic;
    }
    
    protected void checkUpdateAvailability() {
        UpdateChecker uc = new UpdateChecker();
        uc.execute();
    }
    
    public void innerWindowChanged() {
        JInternalFrame inner = desk.getSelectedFrame();
        if (inner == null) {
//            logger.debug("pas de fenetre ouverte");
            actExporter.setEnabled(false);
            actExporterCsv.setEnabled(false);
        } else {
            if (inner instanceof SearchWindow) {
                SearchWindow sw = (SearchWindow) inner;
                boolean canExport = true;
                canExport &= sw.canExport();
//                canExport &= getConnectedUser().hasCollectivite(sw.getCurrentCollectivite().key);
                canExport &= getConnectedUser().hasRole(XemeliosUser.ROLE_EXPORT);
                canExport &= sw.isEtatExportable();
                actExporter.setEnabled(canExport);
                actExporterCsv.setEnabled(canExport);
            }
        }
    }
    
    public void msg(String message) {
        statusField.setText(message);
    }
    
    public static final void main(String[] args) {
        new MainWindow(Constants.NOM_APP, new EmptyStartListener(), args);
    }
    
    public void startWaiter() {
        waiter.startProgress();
    }
    
    public void stopWaiter() {
        waiter.stopProgress();
    }
    
    private void advancedCleanUp() {
        new DlgCleanDatabase(this, true, docModels,getConnectedUser());
//        new DlgCleanDatabaseOld(this, docModels);
    }
    
    private void starterImport(String[] args) {
        if (startListener instanceof Splash) {
            Splash splash = (Splash) startListener;
            splash.setAlwaysOnTop(false);
            splash.setVisible(false);
        }
        if(connectedUser.hasRole(XemeliosUser.ROLE_IMPORT)) {
            for (String fName : args) {
                File f = null;
                try {
                    f = new File(fName);
                } catch (Throwable t) {
                }
                if (f != null && f.exists()) {
                    // le fichier existe, il faut choisir le type de document
                    AskDocumentType adt = new AskDocumentType(this, fName,docModels);
                    adt.setVisible(true);
                    DocumentModel dm = adt.getDocumentType();
                    if (dm != null) {
                        try {
                            Class clazz = Class.forName(dm.getImportClass());
                            Constructor cc = clazz.getConstructor(XemeliosUser.class);
                            Object obj = cc.newInstance(getConnectedUser());
                            if (!(obj instanceof EtatImporteur)) {
                                JOptionPane.showMessageDialog(this,
                                        "Cette classe n'est pas un importeur.\n" +
                                        "Le fichier de configuration qui vous a été livré est certainement invalide.\n" +
                                        "Veuillez contacter votre fournisseur.","Erreur", JOptionPane.ERROR_MESSAGE);
                                return;
                            }
                            EtatImporteur ei = (EtatImporteur) obj;
                            File[] fToImport = new File[] { f };
                            ImportServiceSwingProvider issp = new ImportServiceSwingProvider(this,ei,fToImport.length);
                            ei.setImpSvcProvider(issp);
                            ei.setDocument(dm);
                            //
                            waiter.startProgress();
                            ei.setFilesToImport(fToImport);
                            ei.setPjs(new ArrayList<PJRef>());
                            ei.execute();
                            waiter.stopProgress();
                        } catch (Exception ex) {
                            logger.error("starterImport", ex);
                            /*DisplayExceptionDlg dlg = */
                            new DisplayExceptionDlg(
                                    this, ex);
                        } finally {
                            waiter.stopProgress();
                        }
                    }
                }
            }
//        } else {
//            JOptionPane.showMessageDialog(this,"Vous n'avez pas les droits suffisants pour importer ce fichier.","Erreur",JOptionPane.ERROR_MESSAGE);
        }
        if (startListener instanceof Splash) {
            Splash splash = (Splash) startListener;
            splash.setAlwaysOnTop(false);
            splash.setVisible(false);
        }
    }
    
    public SavedRequestsModel getSavedRequest(ElementModel el) {
        return config.getSavedRequests(el);
    }
    
    protected void savePersonnalConfig() {
        File f = new File(System.getProperty("user.home"));
        File fProp = new File(f, Constants.PERSONNAL_CONFIG_FILENAME);
        //FileOutputStream fw = null;
        OutputStreamWriter w = null;
        try {
//            XmlOutputter xo = new XmlOutputter(fw = new FileOutputStream(fProp), "ISO-8859-1");
            Charset cs = Charset.forName("ISO-8859-1");
            XmlOutputter xo = new XmlOutputter(w = new OutputStreamWriter(new FileOutputStream(fProp), cs),cs.name());
            config.marshall(xo);
        } catch (Throwable t) {
            logger.error("savePersonnalConfig", t);
        } finally {
            if (w != null) {
                try {
                    w.flush(); w.close();
                } catch (Throwable t) {
                }
            }
        }
    }
    
    protected void loadPersonnalConfig() {
        File f = new File(System.getProperty("user.home"));
        File fProp = new File(f, Constants.PERSONNAL_CONFIG_FILENAME);
        config = new PersonnalConfigurationModel(PersonnalConfigurationModel.QN);
        if (fProp.exists()) {
            try {
                PersonnalConfigParser parser = new PersonnalConfigParser();
                parser.parse(fProp);
                config = (PersonnalConfigurationModel) parser.getMarshallable();
            } catch (Throwable t) {
                logger.error("loadPersonnalConfig", t);
            }
        }
        
        // looking for shared requests files
        File[] sharedFiles = f.listFiles(new FilenameFilter() {
            public boolean accept(File dir, String name) {
                return name.startsWith(Constants.PERSONNAL_CONFIG_FILENAME.substring(0, Constants.PERSONNAL_CONFIG_FILENAME.indexOf('.'))+"-");
            }
        });
        for(File share:sharedFiles) {
            try {
                PersonnalConfigParser parser = new PersonnalConfigParser();
                parser.parse(fProp);
                PersonnalConfigurationModel sharedConfig = (PersonnalConfigurationModel) parser.getMarshallable();
                String name = share.getName().substring(Constants.PERSONNAL_CONFIG_FILENAME.indexOf('.')+1);
                name = name.substring(0,name.indexOf('.'));
                sharedConfig.setUnWritable(name);
                config.merge(sharedConfig);
            } catch (Throwable t) {
                logger.error("loadPersonnalConfig", t);
            }
        }
    }
    protected Properties loadCriteriasInputValues() {
        Properties props = new Properties();
        File f = new File(System.getProperty("user.home"));
        File fProp = new File(f, Constants.CRITERIAS_INPUTS_FILENAME);
        if(fProp.exists()) {
            try {
                FileInputStream fis = new FileInputStream(fProp);
                props.load(fis);
                fis.close();
            } catch(IOException ioEx) {}
        }
        return props;
    }
    protected void saveCriteriasInputValues() {
        File f = new File(System.getProperty("user.home"));
        File fProp = new File(f, Constants.CRITERIAS_INPUTS_FILENAME);
        try {
            FileOutputStream fos = new FileOutputStream(fProp);
            criteriasInputValues.store(fos,null);
            fos.close();
        } catch(IOException ioEx) {}
    }
    
    protected void browse(DocumentModel dm, EtatModel emParam,String collectivite, String budget, String specialKey1,String specialKey2) {
        EtatModel em = emParam == null ? dm.getDefaultBrowsableEtat() : emParam;
        NavigationContext nc = new NavigationContext(dm.getId(),em.getId(),em.getBrowsableElement().getId(),collectivite,budget,specialKey1,specialKey2,null,null);
        try {
            if(em==null) throw new DataConfigurationException("No default browsable etat for "+dm.getId());
            DataImpl impl = DataLayerManager.getImplementation();
            ArrayList<String> documents = impl.getDocumentListFromSpecialKeys(em.getBrowsableElement(),collectivite,budget,specialKey1,specialKey2,null,getConnectedUser());
            if(documents.size()==0) {
                JOptionPane.showMessageDialog(this,"Le document est introuvable","",JOptionPane.WARNING_MESSAGE);
            } else if(documents.size()>1) {
                JOptionPane.showMessageDialog(this,"Plusieurs documents répondant à ces critères ont été trouvés.","Erreur",JOptionPane.ERROR_MESSAGE);
            } else {
                String docName = documents.get(0);
                String content = impl.getDocumentAsString(em,new Pair(collectivite,null),new Pair(budget,null),docName,getConnectedUser());
//                logger.debug(content);
                String encoding = IoUtils.getEncoding(content);
                File f = new File(new File(new File(System.getProperty("user.home")),"xemelios/documents-def-ovrrd"),em.getXslt());
                if(!f.exists()) f = new File(new File(em.getParent().getBaseDirectory()),em.getXslt());
                //InputStream is = IoUtils.getInputStream(em.getXslt());
                InputStream is = new FileInputStream(f);
                Hashtable<String,Object> parameters = new Hashtable<String,Object>();
                parameters.put(HtmlViewer.PARAM_DESTINATION,em.useExternalBrowser()?HtmlViewer.VALUE_DESTINATION_EXTERNAL:HtmlViewer.VALUE_DESTINATION_INTERNAL);
                InputStream htmlStream = DematTransform.transform(new ByteArrayInputStream(content.getBytes(encoding)),is,encoding,parameters);
                displayHtmlStream(htmlStream,em,desk,nc);
            }
        } catch (Throwable t) {
            JOptionPane.showMessageDialog(this, t.getMessage(), "Erreur",
                    JOptionPane.ERROR_MESSAGE);
            t.printStackTrace();
        }
    }
    
    public void displayHtmlStream(InputStream is, EtatModel em, JDesktopPane desk) throws IOException {
        displayHtmlStream(is, em, desk, null);
    }
    public void displayHtmlStream(File f, EtatModel em) {
        try {
            String prefix = System.getProperty(Constants.SYS_PROP_NAVIGATOR_PREFIX);
            if (prefix == null)
                prefix = "";
            String url = f.getCanonicalPath();
            if (em.useExternalBrowser()) {
                StartUrl.startUrl(url);
            } else {
                HtmlViewer viewer = new HtmlViewer(f.toURL(), "Détail " + em.getTitre(), null, this);
                desk.add(viewer);
                desk.getDesktopManager().maximizeFrame(viewer);
                viewer.show();
            }
        } catch (Exception toEx) {
            Component comp2 = desk;
            while (comp2 != null && !(comp2 instanceof JFrame))
                comp2 = comp2.getParent();
            if (comp2 != null)
                new DisplayExceptionDlg((JFrame) comp2, toEx);
            else
                toEx.printStackTrace();
        }
        
    }
    public void displayHtmlStream(InputStream is, EtatModel em, JDesktopPane desk, NavigationContext nc) throws IOException {
        File tmpDir = new File(System.getProperty("java.io.tmpdir"));
        tmpDir = new File(tmpDir, Constants.NOM_APP);
        tmpDir.mkdirs();
        File tmp = File.createTempFile("xemelios-data-", ".html", tmpDir);
        BufferedInputStream bis = new BufferedInputStream(is);
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(tmp));
        byte[] buff = new byte[1024];
        int lengthRead = bis.read(buff);
        while (lengthRead > 0) {
            bos.write(buff, 0, lengthRead);
            lengthRead = bis.read(buff);
        }
        bos.flush();
        bos.close();
        try {
            String prefix = System.getProperty(Constants.SYS_PROP_NAVIGATOR_PREFIX);
            if (prefix == null)
                prefix = "";
            String url = tmp.getCanonicalPath();
            if (em.useExternalBrowser()) {
                StartUrl.startUrl(url);
            } else {
                HtmlViewer viewer = new HtmlViewer(tmp.toURL(), "Détail "+ em.getTitre(), nc, this);
                desk.add(viewer);
                desk.getDesktopManager().maximizeFrame(viewer);
                viewer.show();
            }
        } catch (Exception toEx) {
            Component comp2 = desk;
            while (comp2 != null && !(comp2 instanceof JFrame))
                comp2 = comp2.getParent();
            if (comp2 != null)
                new DisplayExceptionDlg((JFrame) comp2, toEx);
            else
                toEx.printStackTrace();
        }
    }
    
    protected void initDataLayer() throws DataConfigurationException {
        logger.debug("entering initDataLayer");
        String availableLayers = System.getProperty(Constants.SYS_PROP_AVAILABLE_LAYERS);
        StringTokenizer tokenizer = new StringTokenizer(availableLayers, ",");
        while(tokenizer.hasMoreTokens()) {
            String className = tokenizer.nextToken();
            logger.debug("instanciating "+className);
            try {
                Class.forName(className);
            } catch (Throwable t) {
                logger.error("error while initializing data layer class ("+className+") :", t);
            }
        }
        logger.debug("setting dataImpl to "+System.getProperty(Constants.SYS_PROP_DATA_IMPL));
        try {
            DataLayerManager.setDataImpl(System.getProperty(Constants.SYS_PROP_DATA_IMPL));
        } catch(DataConfigurationException dcEx) {
            if(getStartListener() instanceof Splash) {
                Splash splash = (Splash)startListener;
                splash.setAlwaysOnTop(false);
                splash.setVisible(false);
            }
            JOptionPane.showMessageDialog(null,dcEx.getMessage(),"Erreur de connexion au serveur Xemelios",JOptionPane.ERROR_MESSAGE);
            if(getStartListener() instanceof Splash) {
                Splash splash = (Splash)startListener;
                splash.setVisible(true);
                splash.setAlwaysOnTop(true);
            }
            throw dcEx;
        }
        logger.debug("exiting successfully initDataLayer");
    }
    
    private void reloadConfiguration(){
        PropertiesExpansion pe1 = new PropertiesExpansion();
        for(Object key:System.getProperties().keySet()) {
            pe1.setProperty((String)key,System.getProperty((String)key));
        }
        Loader.flushLoadedConfig();
        docModels = getDocumentsInfos(pe1.getProperty(Constants.SYS_PROP_DOC_DEF_DIR));
        try {
            DataLayerManager.getImplementation().reset();
        } catch (DataConfigurationException e) {
            e.printStackTrace();
            new DisplayExceptionDlg(this,e);
        }
    }
    
    public XemeliosUser getConnectedUser() { return connectedUser; }
    /**
     * Reads and execute update script if it is present
     *
     */
    protected void checkUpdateScript() {
        File f = new File(System.getProperty(Constants.SYS_PROP_XEMELIOS_PRG),"update-script.xml");
        if(f.exists()) {
            try {
                DocumentBuilder domBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
                XPath xpath = xpf.newXPath();
                Document dom = domBuilder.parse(f);
                NodeList nl = (NodeList)xpath.evaluate("/update-script/*",dom,XPathConstants.NODESET);
                for(int i=0;i<nl.getLength();i++) {
                    Node node = nl.item(i);
                    if(node.getNodeType()==Node.ELEMENT_NODE) {
                        String nodeName = node.getNodeName();
                        if("component".equals(nodeName)) {
                            String id=xpath.evaluate("./@id",node);
                            String version=xpath.evaluate("./@version",node);
                            String description=xpath.evaluate("./@description",node);
                            installComponent(id,version,description);
                        } else if("configurator".equals(nodeName)) {
                            String className = xpath.evaluate("./@class",node);
                            PropertiesExpansion props = new PropertiesExpansion(System.getProperties());
                            NodeList params = (NodeList)xpath.evaluate("./param",node,XPathConstants.NODESET);
                            for(int paramCount=0;paramCount<params.getLength();paramCount++) {
                                Node p = params.item(paramCount);
                                String paramName = xpath.evaluate("./@name",p);
                                String paramValue = xpath.evaluate("./text()",p);
                                props.put(paramName, paramValue);
                            }
                            executeConfigurator(className,props);
                        } else if("remove-component".equals(nodeName)){
                            // TODO: implement this
                        } else {
                            logger.warn("update-script: unexpected element <"+nodeName+"/>");
                        }
                    }
                }
            } catch(Throwable t) {
                new DisplayExceptionDlg((Frame)null,t);
            }
            f.delete();
            
            // on recharge les propriétés au cas où le configurateur ait changé le xemelios.properties
//            File xemPropsFile = new File(pe.replace("${xemelios.prg}/root/xemelios.properties"));
//            PropertiesExpansion props = new PropertiesExpansion();
//            try {
//                props.load(new FileInputStream(xemPropsFile));
//            } catch(IOException ioEx) {
//                // on ne fait rien
//            }
//            for(Object key:props.keySet()) {
//                System.setProperty((String)key, props.replace((String)key));
//            }
//            pe = new PropertiesExpansion();
//            for(Object key:System.getProperties().keySet()) {
//                pe.setProperty((String)key,System.getProperty((String)key));
//            }
//            componentsModelFileName=pe.getProperty(Constants.SYS_PROP_COMPONENTS_FILENAME);
//            updateUrl=pe.getProperty(Constants.SYSPROP_UPDATE_SOURCE);
//            docModels = getDocumentsInfos(pe.getProperty(Constants.SYS_PROP_DOC_DEF_DIR));
        }
    }
    /**
     * This methods registers or updates a new component in components-file.
     * @param id
     * @param version
     * @param description
     * @throws ParserConfigurationException
     * @throws SAXException
     * @throws IOException
     * @throws XPathExpressionException
     */
    protected void installComponent(String id,String version,String description) throws ParserConfigurationException, SAXException, IOException, XPathExpressionException {
        // TODO: rewrite this correctly
        logger.info("installing component "+id);
        XemeliosComponentModel installedComponents = null;
        File f = new File(System.getProperty("xemelios.components.filename"));
        if(f.exists()) {
            ComponentsParser parser = new ComponentsParser();
            parser.parse(f);
            installedComponents = (XemeliosComponentModel)parser.getMarshallable();
        } else {
            installedComponents = new XemeliosComponentModel(XemeliosComponentModel.QN);
        }
        ComponentModel cm = new ComponentModel(id,version,description);
        String cVersion = installedComponents.getComponentVersion(cm.getId());
        if(cVersion==null) {
            installedComponents.addChild(cm,ComponentModel.QN);
        } else {
            installedComponents.remove(cm.getId());
            installedComponents.addChild(cm,ComponentModel.QN);
        }
        OutputStreamWriter fos = new OutputStreamWriter(new FileOutputStream(f),"ISO-8859-1");
        XmlOutputter output = new XmlOutputter(fos,"ISO-8859-1");
        installedComponents.marshall(output);
        fos.flush(); fos.close();
    }
    protected void executeConfigurator(String className, PropertiesExpansion props) throws Exception {
        logger.info("executing configurator "+className);
        Class clazz = Class.forName(className);
        Constructor cons = clazz.getConstructor(new Class[0]);
        Object o = cons.newInstance(new Object[0]);
        if(o instanceof AbstractConfigurator) {
            AbstractConfigurator ac = (AbstractConfigurator)o;
            ac.setProperties(props);
            ac.setMainWindow(this);
            ac.doConfigure();
        } else {
            throw new Exception(className+" does not extends "+AbstractConfigurator.class.getName());
        }
    }
    public String getCriteriaInputValue(String criteriaId) {
        if(criteriaId==null) return null;
        return (String)criteriasInputValues.get(criteriaId);
    }
    public synchronized void setCriteriaInputValue(String criteriaId,String value) {
        if(criteriaId==null) return;
        if(value!=null && value.length()>0) criteriasInputValues.put(criteriaId,value);
        else criteriasInputValues.remove(criteriaId);
    }
    private static class EmptyStartListener implements StartListener {
        public void notifyMessage(String msg) {
        }
        
        public void notifyProgress(int progress) {
        }
        
        public void pushProgress() {
        }
        
        public void completeProgress() {
        }
        
        public void setVisible(boolean visible) {
        }
    }
    
    private final class ActionImport extends AbstractAction {
        /**
         * Comment for <code>serialVersionUID</code>
         */
        private static final long serialVersionUID = 3833182540793198128L;
        
        String docId, etatId;
        
        public ActionImport(String titre, String docId) {
            super(titre);
            this.docId = docId;
        }
        
        public void actionPerformed(ActionEvent evt) {
            importer(docId);
        }
    }
    
    private final class ActionControl extends AbstractAction {
        /**
         * Comment for <code>serialVersionUID</code>
         */
        private static final long serialVersionUID = 3833182540793198128L;
        
        String docId, etatId;
        
        public ActionControl(String titre, String docId) {
            super(titre);
            this.docId = docId;
        }
        
        public void actionPerformed(ActionEvent evt) {
            controller(docId);
        }
    }
    
    private final class ActionSearch extends AbstractAction {
        /**
         * Comment for <code>serialVersionUID</code>
         */
        private static final long serialVersionUID = 3617860763465560632L;
        
        String docId, etatId, elementId;
        
        public ActionSearch(String titre, String docId, String etatId, String elementId) {
            super(titre);
            this.docId = docId;
            this.etatId = etatId;
            this.elementId = elementId;
        }
        
        public void actionPerformed(ActionEvent evt) {
            search(docId, etatId, elementId);
        }
    }
    
    public DocumentsModel getDocModels() {
        return docModels;
    }
    
    protected void setMenusAccess() {
        final Runnable updater = new Runnable() {
            public void run() {
                searchMenu.setEnabled(getConnectedUser().hasRole(XemeliosUser.ROLE_SEARCH));
                browseMenu.setEnabled(getConnectedUser().hasRole(XemeliosUser.ROLE_BROWSE));
                if (importerMenu != null) {
                    importerMenu.setEnabled(getConnectedUser().hasRole(XemeliosUser.ROLE_IMPORT));
                }
                if (controlerMenu != null) {
                    controlerMenu.setEnabled(getConnectedUser().hasRole(XemeliosUser.ROLE_IMPORT));
                }
                if (mnuClean != null)
                    mnuClean.setEnabled(getConnectedUser().hasRole(XemeliosUser.ROLE_CLEAN));
                if (mnuCleanPro != null)
                    mnuCleanPro.setEnabled(getConnectedUser().hasRole(XemeliosUser.ROLE_CLEAN));
                actExporter.setEnabled(getConnectedUser().hasRole(XemeliosUser.ROLE_EXPORT));
                actExporterCsv.setEnabled(getConnectedUser().hasRole(XemeliosUser.ROLE_EXPORT));
                if (exportMenu != null)
                    exportMenu.setEnabled(getConnectedUser().hasRole(XemeliosUser.ROLE_EXPORT));
            }
        };
        SwingUtilities.invokeLater(updater);
    }
    
    public StartListener getStartListener() {
        return startListener;
    }
    
    public static MainWindow getInstance() {
        return instance;
    }
    
    @Override
    protected void finalize() {
        instance = null;
        try {
            if (DataLayerManager.getImplementation() != null) {
                DataLayerManager.getImplementation().shutdown();
            }
        } catch(DataConfigurationException dce) {
            logger.error(dce, dce);
        }
    }
    
    protected void applyPatches() {
        AbstractConfigurator ac = null;
        try {
            Class clazz = Class.forName("fr.gouv.finances.cp.xemelios.patches.Core4000");
            ac = (AbstractConfigurator)clazz.newInstance();
        } catch(Throwable t) {
            // ignore
        }
        if(ac!=null) {
            try {
                PropertiesExpansion props = new PropertiesExpansion(System.getProperties());
                ac.setProperties(props);
                ac.run();
            } catch(Exception ex) {
                logger.error("while applying patches",ex);
                System.exit(2);
            }
        }

    }
    public static class ImportContent {
        ArrayList<File> filesToImport;
        ArrayList<PJRef> pjs;
        public ImportContent() {
            super();
            filesToImport = new ArrayList<File>();
            pjs = new ArrayList<PJRef>();
        }
        public void setFilesToImport(ArrayList<File> array){
            this.filesToImport=array;
        }
        public ArrayList<File> getFilesToImport() { return filesToImport; }
        public ArrayList<PJRef> getPjs() { return pjs; }
    }
    public JDesktopPane getDesk() { return desk; }
    
    private void configExport() {
        DlgSearchConfig sc = new DlgSearchConfig(this,docModels, DlgSearchConfig.EXEC_FROM_CONF,getConnectedUser());
        sc.setVisible(true);
    }
    
    private void launchExport() {
        DlgExportAll dga = new DlgExportAll(this, true, docModels,getConnectedUser());
        dga.setVisible(true);
    }
    
    public class UpdateChecker extends SwingWorker<Boolean,Object> {
        protected Boolean doInBackground() {
            UpdaterUtil uu = new UpdaterUtil(MainWindow.instance.updateUrl,MainWindow.instance.componentsModelFileName);
            return new Boolean(uu.areThereUpdatesAvailable());
        }
        @Override
        protected void done() {
            try {
                if(get().booleanValue()) {
                    String message = "<html>Des mises à jour sont disponibles.<br><br>Pour les installer, allez dans<br>le menu Fichier / Mises à jour,<br>puis cliquez sur Rechercher.</html>";
                    JOptionPane.showMessageDialog(MainWindow.instance,message,"Information",JOptionPane.INFORMATION_MESSAGE);
                }
            } catch(Throwable t) {}
        }
    }
    
    protected JMenu loadToolMenu() {
logger.debug("looking for tools");
        JMenu ret = new JMenu("Outils");
        PropertiesExpansion pe1 = new PropertiesExpansion();
        pe1.putAll(System.getProperties());
        // load components list
        XemeliosComponentsModel components = null;
        try {
            XemeliosComponentsParser parser = new XemeliosComponentsParser();
            parser.parse(new File(pe1.getProperty(Constants.SYS_PROP_COMPONENTS_FILENAME)));
            components = (XemeliosComponentsModel)parser.getMarshallable();
        } catch(Throwable t) {}
        if(components==null) components = new XemeliosComponentsModel(XemeliosComponentsModel.QN);
        String sDir = pe1.getProperty(Constants.SYS_PROP_XEMELIOS_TOOLS_DIR);
        if(sDir!=null) {
            String[] directories = sDir.split(",");
            for(String currentDirectory:directories) {
                // replacing ${....} variables
                currentDirectory=pe1.replace(currentDirectory);
logger.debug("looking for tool into "+currentDirectory);
                File dir = new File(currentDirectory);
                if(dir.exists() && dir.isDirectory()) {
                    File[] jars = dir.listFiles(new java.io.FileFilter() {
                        public boolean accept(File pathname) {
                            return (pathname.getName().toUpperCase().endsWith(".JAR"));
                        }
                    });
                    for(File jar:jars) {
logger.debug("found "+jar.getAbsolutePath());
                        try {
                            JarFile jf = new JarFile(jar);
                            Manifest mn = jf.getManifest();
                            String toolClassName = mn.getMainAttributes().getValue(ToolConstants.TOOL_CLASS);
                            String toolRequires = mn.getMainAttributes().getValue(ToolConstants.TOOL_REQUIRES);
                            //String toolDescription = mn.getMainAttributes().getValue(ToolConstants.TOOL_DESC);
                            String toolDescription = null;
                            String toolIcon = mn.getMainAttributes().getValue(ToolConstants.TOOL_ICON);
                            jf.close();
                            if(toolClassName!=null && toolClassName.length()>0) {
                                try {
                                    if(toolRequires!=null && toolRequires.length()>3) {
                                        String[] requiredComps = toolRequires.split(";");
                                        for(String comp:requiredComps) {
                                            String[] compDetails = comp.split(",");
                                            if(compDetails.length==2) {
                                                fr.gouv.finances.dgfip.xemelios.common.components.ComponentModel installedComp = components.getComponent(compDetails[0]);
                                                if(installedComp==null) throw new Exception("Component "+compDetails[0]+" ("+compDetails[1]+") required");
                                                if(!installedComp.isNewerOrEqualThan(compDetails[1])) throw new Exception(installedComp.getId()+" "+installedComp.getVersion()+") to old: "+compDetails[1]+" required");
                                            }
                                        }
                                    }
                                    // tout est OK, on ajoute le jar au classpath
                                    if(!"true".equals(pe1.getProperty(Constants.SYS_PROP_XEMELIOS_AVOID_TOOL_JAR_LOADING))) {
                                        Bootstrap.addFile(jar);
                                    }
                                    Class clazz = null;
                                    try {
                                        clazz = Class.forName(toolClassName);
                                        @SuppressWarnings("unchecked")
                                        MenuTitle mt = (MenuTitle)clazz.getAnnotation(MenuTitle.class);
                                        if(mt!=null)
                                            toolDescription = mt.value();
                                        else
                                            logger.error("Plugin Class "+toolClassName+" doesn't provide annotation @MenuTitle");
                                    } catch(Exception ex) {
                                        logger.error(ex.getMessage(),ex);
//                                        ex.printStackTrace();
                                    }
                                    if(toolDescription!=null && toolDescription.length()>0) {
                                        Action a = null;
                                        if(toolIcon!=null) {
                                            ImageIcon img = IhmFactory.getIconFromResource(toolIcon);
                                            if(img!=null) a = new ActionPlugin(toolClassName, toolDescription,img);
                                            else a = new ActionPlugin(toolClassName, toolDescription);
                                        } else {
                                            a = new ActionPlugin(toolClassName, toolDescription);
                                        }
                                        JMenuItem mnuItem = new JMenuItem(a);
                                        ret.add(mnuItem);
                                    }
                                } catch(Exception ex) {
                                    logger.info("can not install plugin "+toolClassName+": "+ex.getMessage());
                                    continue;
                                }
                            }
                        } catch(Throwable t) {}
                    }
                }
            }
            if(ret.getItemCount()>0)
                return ret;
        }
        return null;
    }
    public PropertiesExpansion getEnvProperties() { return pe; }
    private class ActionPlugin extends AbstractAction {
        private static final long serialVersionUID = 1L;
        private String className = null;
        public ActionPlugin(String className, String desc) {
            super(desc);
            this.className = className;
        }
        public ActionPlugin(String className, String desc, Icon icon) {
            super(desc,icon);
            this.className=className;
        }
        
        public void actionPerformed(ActionEvent e) {
            try {
                Class clazz = Class.forName(className);
                Object o = clazz.newInstance();
                if(o instanceof AbstractTool) {
                    AbstractTool at = (AbstractTool)o;
                    at.run();
                } else {
                    throw new Exception(className+" does not extends "+AbstractTool.class.getName());
                }
            } catch(Exception ex) {
                new DisplayExceptionDlg(MainWindow.instance,ex);
            }
        }
        
    }
    private class PoolStatDisplayer extends JPanel {
        private static final long serialVersionUID = 1L;
        private JLabel lblActive=null, lblIdle = null;
        public PoolStatDisplayer() {
            super();
            add(new JLabel("Active(s): "));
            add(lblActive = new JLabel("0"));
            add(new JLabel("  Idle(s): "));
            add(lblIdle=new JLabel("0"));
            Timer timer = new Timer(500,new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    try {
//                        DataImpl di = DataLayerManager.getImplementation();
                        if(PoolManager.getInstance()!=null && PoolManager.getInstance().getPool()!=null) {
                            Pool pool = PoolManager.getInstance().getPool();
                            lblActive.setText(Integer.toString(pool.getNumActive()));
                            lblIdle.setText(Integer.toString(pool.getNumIdle()));
                        }
                    } catch(Exception ex) {
                        // TODO
                    }
                }
            });
            timer.start();
        }
    }

    private class XemeliosArchiveImporterWorker extends SwingWorker<XemeliosArchiveImporter.Errors,String> {
        private XemeliosArchiveImporter importer;
        private Errors errors;
        public XemeliosArchiveImporterWorker(XemeliosArchiveImporter importer) {
            super();
            this.importer=importer;
        }

        @Override
        protected Errors doInBackground() throws Exception {
            errors = importer.doImport();
            return errors;
        }

        @Override
        protected void done() {
            SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
            logger.debug("Fin import archive a "+sdf.format(new Date()));
            StringBuilder sb = new StringBuilder();
            if(errors!=null) {
                for(XemeliosArchiveImporter.Error error:errors.getErrors()) {
                    sb.append("<").append(error.getSeverityMessage()).append("> ").append(error.getMessage()).append("\n");
                }
            }
            if(sb.length()>0) {
                JOptionPane.showMessageDialog(MainWindow.this, sb.toString());
            } else {
                // pas d'erreur
                JOptionPane.showMessageDialog(MainWindow.this,importer.getFileInfo().toString(docModels));
            }

        }


    }

}
