@Override
 public void finish() {
   for (final Ambience ambience : AmbienceWizard.ambiences) {
     AmbienceManager.getInstance().registerAmbience(ambience);
   }
   // commit it to avoid it is lost before the app close
   AmbienceManager.getInstance().commit();
   // Refresh UI
   ObservationManager.notify(new JajukEvent(JajukEvents.AMBIENCES_CHANGE));
   InformationJPanel.getInstance()
       .setMessage(Messages.getString("Success"), InformationJPanel.MessageType.INFORMATIVE);
 }
 /**
  * Display currently copied file to information panel.
  *
  * @param file
  */
 private void showMessage(java.io.File file) {
   String message = Messages.getString("Device.45");
   message += file.getAbsolutePath() + "]";
   InformationJPanel.getInstance().setMessage(message, InformationJPanel.MessageType.INFORMATIVE);
 }
 /** Synchronize action itself. */
 void synchronizeCommand() {
   try {
     bAlreadySynchronizing = true;
     long lTime = System.currentTimeMillis();
     int iNbCreatedFilesDest = 0;
     int iNbCreatedFilesSrc = 0;
     lVolume = 0;
     final boolean bidi =
         getValue(Const.XML_DEVICE_SYNCHRO_MODE).equals(Const.DEVICE_SYNCHRO_MODE_BI);
     // check this device is synchronized
     final String sIdSrc = (String) getValue(Const.XML_DEVICE_SYNCHRO_SOURCE);
     if (StringUtils.isBlank(sIdSrc) || sIdSrc.equals(getID())) {
       // cannot synchro with itself
       return;
     }
     final Device dSrc = DeviceManager.getInstance().getDeviceByID(sIdSrc);
     // perform a fast refresh
     refreshCommand(false, true, null);
     // if bidi sync, refresh the other device as well (new file can
     // have been copied to it)
     if (bidi) {
       dSrc.refreshCommand(false, true, null);
     }
     // start message
     InformationJPanel.getInstance()
         .setMessage(
             new StringBuilder(Messages.getString("Device.31"))
                 .append(dSrc.getName())
                 .append(',')
                 .append(getName())
                 .append("]")
                 .toString(),
             InformationJPanel.MessageType.INFORMATIVE);
     // in both cases (bi or uni-directional), make an unidirectional
     // sync from source device to this one
     iNbCreatedFilesDest = synchronizeUnidirectonal(dSrc, this);
     // now the other one if bidi
     if (bidi) {
       iNbCreatedFilesDest += synchronizeUnidirectonal(this, dSrc);
     }
     // end message
     lTime = System.currentTimeMillis() - lTime;
     final String sOut =
         new StringBuilder(Messages.getString("Device.33"))
             .append(((lTime < 1000) ? lTime + " ms" : lTime / 1000 + " s"))
             .append(" - ")
             .append(iNbCreatedFilesSrc + iNbCreatedFilesDest)
             .append(Messages.getString("Device.35"))
             .append(lVolume / 1048576)
             .append(Messages.getString("Device.36"))
             .toString();
     // perform a fast refresh
     refreshCommand(false, true, null);
     // if bidi sync, refresh the other device as well (new file can
     // have been copied to it)
     if (bidi) {
       dSrc.refreshCommand(false, true, null);
     }
     InformationJPanel.getInstance().setMessage(sOut, InformationJPanel.MessageType.INFORMATIVE);
     Log.debug(sOut);
   } catch (final RuntimeException e) {
     // runtime errors are thrown
     throw e;
   } catch (final Exception e) {
     // and regular ones logged
     Log.error(e);
   } finally {
     // make sure to unlock synchronizing even if an error occurred
     bAlreadySynchronizing = false;
     // Refresh GUI
     ObservationManager.notify(new JajukEvent(JajukEvents.DEVICE_REFRESH));
   }
 }
 /**
  * Synchronize a device with another one (unidirectional).
  *
  * @param dSrc
  * @param dest
  * @return nb of created files
  */
 private int synchronizeUnidirectonal(final Device dSrc, final Device dest) {
   final Set<Directory> hsSourceDirs = new HashSet<Directory>(100);
   // contains paths ( relative to device) of desynchronized dirs
   final Set<String> hsDesynchroPaths = new HashSet<String>(10);
   final Set<Directory> hsDestDirs = new HashSet<Directory>(100);
   int iNbCreatedFiles = 0;
   List<Directory> dirs = DirectoryManager.getInstance().getDirectories();
   for (Directory dir : dirs) {
     if (dir.getDevice().equals(dSrc)) {
       // don't take desynchronized dirs into account
       if (dir.getBooleanValue(Const.XML_DIRECTORY_SYNCHRONIZED)) {
         hsSourceDirs.add(dir);
       } else {
         hsDesynchroPaths.add(dir.getRelativePath());
       }
     }
   }
   for (Directory dir : dirs) {
     if (dir.getDevice().equals(dest)) {
       if (dir.getBooleanValue(Const.XML_DIRECTORY_SYNCHRONIZED)) {
         // don't take desynchronized dirs into account
         hsDestDirs.add(dir);
       } else {
         hsDesynchroPaths.add(dir.getRelativePath());
       }
     }
   }
   // handle known extensions and image files
   final FileFilter filter =
       new JajukFileFilter(
           false,
           new JajukFileFilter[] {KnownTypeFilter.getInstance(), ImageFilter.getInstance()});
   for (Directory dir : hsSourceDirs) {
     // give a chance to exit during sync
     if (ExitService.isExiting()) {
       return iNbCreatedFiles;
     }
     boolean bNeedCreate = true;
     final String sPath = dir.getRelativePath();
     // check the directory on source is not desynchronized. If it
     // is, leave without checking files
     if (hsDesynchroPaths.contains(sPath)) {
       continue;
     }
     for (Directory dir2 : hsDestDirs) {
       if (dir2.getRelativePath().equals(sPath)) {
         // directory already exists on this device
         bNeedCreate = false;
         break;
       }
     }
     // create it if needed
     final File fileNewDir = new File(new StringBuilder(dest.getUrl()).append(sPath).toString());
     if (bNeedCreate && !fileNewDir.mkdirs()) {
       Log.warn("Could not create directory " + fileNewDir);
     }
     // synchronize files
     final File fileSrc = new File(new StringBuilder(dSrc.getUrl()).append(sPath).toString());
     final File[] fSrcFiles = fileSrc.listFiles(filter);
     if (fSrcFiles != null) {
       for (final File element : fSrcFiles) {
         File[] filesArray = fileNewDir.listFiles(filter);
         if (filesArray == null) {
           // fileNewDir is not a directory or an error occurred (
           // read/write right ? )
           continue;
         }
         final List<File> files = Arrays.asList(filesArray);
         // Sort so files are copied in the filesystem order
         Collections.sort(files);
         boolean bNeedCopy = true;
         for (final File element2 : files) {
           if (element.getName().equalsIgnoreCase(element2.getName())) {
             bNeedCopy = false;
           }
         }
         if (bNeedCopy) {
           try {
             UtilSystem.copyToDir(element, fileNewDir);
             iNbCreatedFiles++;
             lVolume += element.length();
             InformationJPanel.getInstance()
                 .setMessage(
                     new StringBuilder(Messages.getString("Device.41"))
                         .append(dSrc.getName())
                         .append(',')
                         .append(dest.getName())
                         .append(Messages.getString("Device.42"))
                         .append(element.getAbsolutePath())
                         .append("]")
                         .toString(),
                     InformationJPanel.MessageType.INFORMATIVE);
           } catch (final JajukException je) {
             Messages.showErrorMessage(je.getCode(), element.getAbsolutePath());
             Messages.showErrorMessage(27);
             Log.error(je);
             return iNbCreatedFiles;
           } catch (final Exception e) {
             Messages.showErrorMessage(20, element.getAbsolutePath());
             Messages.showErrorMessage(27);
             Log.error(20, "{{" + element.getAbsolutePath() + "}}", e);
             return iNbCreatedFiles;
           }
         }
       }
     }
   }
   return iNbCreatedFiles;
 }