/**
  * Synchronizing asynchronously.
  *
  * @param bAsynchronous : set asynchronous or synchronous mode
  */
 public void synchronize(final boolean bAsynchronous) {
   // Check a source device is defined
   if (StringUtils.isBlank((String) getValue(Const.XML_DEVICE_SYNCHRO_SOURCE))) {
     Messages.showErrorMessage(171);
     return;
   }
   final Device device = this;
   if (!device.isMounted()) {
     try {
       device.mount(true);
     } catch (final Exception e) {
       Log.error(11, getName(), e); // mount failed
       Messages.showErrorMessage(11, getName());
       return;
     }
   }
   if (bAsynchronous) {
     final Thread t =
         new Thread("Device Synchronize Thread") {
           @Override
           public void run() {
             synchronizeCommand();
           }
         };
     t.setPriority(Thread.MIN_PRIORITY);
     t.start();
   } else {
     synchronizeCommand();
   }
 }
 /**
  * Unmount the device with ejection.
  *
  * @param bEjection set whether the device must be ejected
  * @param bUIRefresh set whether the UI should be refreshed
  */
 public void unmount(final boolean bEjection, final boolean bUIRefresh) {
   // look to see if the device is already mounted
   if (!bMounted) {
     Messages.showErrorMessage(125); // already unmounted
     return;
   }
   // ask fifo if it doens't use any track from this device
   if (!QueueModel.canUnmount(this)) {
     Messages.showErrorMessage(121);
     return;
   }
   bMounted = false;
   if (bUIRefresh) {
     ObservationManager.notify(new JajukEvent(JajukEvents.DEVICE_UNMOUNT));
   }
 }
 /**
  * Change a playlist name
  *
  * @param plfOld
  * @param sNewName
  * @return new playlist
  */
 public Playlist changePlaylistFileName(Playlist plfOld, String sNewName) throws JajukException {
   synchronized (PlaylistManager.getInstance().getLock()) {
     // check given name is different
     if (plfOld.getName().equals(sNewName)) {
       return plfOld;
     }
     // check if this file still exists
     if (!plfOld.getFio().exists()) {
       throw new JajukException(135);
     }
     java.io.File ioNew =
         new java.io.File(
             plfOld.getFio().getParentFile().getAbsolutePath()
                 + java.io.File.separator
                 + sNewName);
     // recalculate file ID
     plfOld.getDirectory();
     String sNewId = PlaylistManager.createID(sNewName, plfOld.getDirectory());
     // create a new playlist (with own fio and sAbs)
     Playlist plfNew = new Playlist(sNewId, sNewName, plfOld.getDirectory());
     plfNew.setProperties(plfOld.getProperties()); // transfert all
     // properties
     // (inc id and
     // name)
     plfNew.setProperty(XML_ID, sNewId); // reset new id and name
     plfNew.setProperty(XML_NAME, sNewName); // reset new id and name
     // check file name and extension
     if (plfNew.getName().lastIndexOf('.') != plfNew.getName().indexOf('.') // just
         // one
         // '.'
         || !(Util.getExtension(ioNew).equals(EXT_PLAYLIST))) { // check
       // extension
       Messages.showErrorMessage(134);
       throw new JajukException(134);
     }
     // check if future file exists (under windows, file.exists
     // return true even with different case so we test file name is
     // different)
     if (!ioNew.getName().equalsIgnoreCase(plfOld.getName()) && ioNew.exists()) {
       throw new JajukException(134);
     }
     // try to rename file on disk
     try {
       plfOld.getFio().renameTo(ioNew);
     } catch (Exception e) {
       throw new JajukException(134);
     }
     // OK, remove old file and register this new file
     hmItems.remove(plfOld.getID());
     if (!hmItems.containsKey(sNewId)) {
       hmItems.put(sNewId, plfNew);
     }
     // change directory reference
     plfNew.getDirectory().changePlaylistFile(plfOld, plfNew);
     return plfNew;
   }
 }
 /**
  * Manual refresh, displays a dialog.
  *
  * @param bAsk ask for refreshing type (deep or fast ?)
  * @param bAfterMove is this refresh done after a device location change ?
  * @param forcedDeep : override bAsk and force a deep refresh
  * @param dirsToRefresh : only refresh specified dirs, or all of them if null
  */
 public void manualRefresh(
     final boolean bAsk,
     final boolean bAfterMove,
     final boolean forcedDeep,
     List<Directory> dirsToRefresh) {
   int i = 0;
   try {
     i = prepareRefresh(bAsk);
     if (i == OPTION_REFRESH_CANCEL) {
       return;
     }
     bAlreadyRefreshing = true;
   } catch (JajukException je) {
     Messages.showErrorMessage(je.getCode());
     Log.debug(je);
     return;
   }
   try {
     reporter = new ManualDeviceRefreshReporter(this);
     reporter.startup();
     // clean old files up (takes a while)
     if (!bAfterMove) {
       cleanRemovedFiles(dirsToRefresh);
     }
     reporter.cleanupDone();
     // Actual refresh
     refreshCommand(((i == Device.OPTION_REFRESH_DEEP) || forcedDeep), true, dirsToRefresh);
     // cleanup logical items
     org.jajuk.base.Collection.cleanupLogical();
     // if it is a move, clean old files *after* the refresh
     if (bAfterMove) {
       cleanRemovedFiles(dirsToRefresh);
     }
     // notify views to refresh
     ObservationManager.notify(new JajukEvent(JajukEvents.DEVICE_REFRESH));
     // Commit collection at each refresh (can be useful if
     // application
     // is closed brutally with control-C or shutdown and that
     // exit hook has no time to perform commit).
     // But don't commit when any device is refreshing to avoid collisions.
     if (!DeviceManager.getInstance().isAnyDeviceRefreshing()) {
       try {
         org.jajuk.base.Collection.commit(SessionService.getConfFileByPath(Const.FILE_COLLECTION));
       } catch (final IOException e) {
         Log.error(e);
       }
     }
   } finally {
     // Do not let current reporter as a manual reporter because it would fail
     // in NPE with auto-refresh
     reporter = null;
     // Make sure to unlock refreshing
     bAlreadyRefreshing = false;
   }
 }
 /**
  * Change a playlist name.
  *
  * @param plfOld DOCUMENT_ME
  * @param sNewName DOCUMENT_ME
  * @return new playlist
  * @throws JajukException the jajuk exception
  */
 public synchronized Playlist changePlaylistFileName(Playlist plfOld, String sNewName)
     throws JajukException {
   // check given name is different
   if (plfOld.getName().equals(sNewName)) {
     return plfOld;
   }
   // check if this file still exists
   if (!plfOld.getFIO().exists()) {
     throw new JajukException(135);
   }
   java.io.File ioNew =
       new java.io.File(
           plfOld.getFIO().getParentFile().getAbsolutePath() + java.io.File.separator + sNewName);
   // recalculate file ID
   plfOld.getDirectory();
   String sNewId = PlaylistManager.createID(sNewName, plfOld.getDirectory());
   // create a new playlist (with own fio and sAbs)
   Playlist plfNew = new Playlist(sNewId, sNewName, plfOld.getDirectory());
   // Transfer all properties (id and name)
   // We use a shallow copy of properties to avoid any properties share between
   // two items
   plfNew.setProperties(plfOld.getShallowProperties());
   plfNew.setProperty(Const.XML_ID, sNewId); // reset new id and name
   plfNew.setProperty(Const.XML_NAME, sNewName); // reset new id and name
   // check file name and extension
   if (plfNew.getName().lastIndexOf('.') != plfNew.getName().indexOf('.') // just
       // one
       // '.'
       || !(UtilSystem.getExtension(ioNew).equals(Const.EXT_PLAYLIST))) { // check
     // extension
     Messages.showErrorMessage(134);
     throw new JajukException(134);
   }
   // check if future file exists (under windows, file.exists
   // return true even with different case so we test file name is
   // different)
   if (!ioNew.getName().equalsIgnoreCase(plfOld.getName()) && ioNew.exists()) {
     throw new JajukException(134);
   }
   // try to rename file on disk
   try {
     boolean result = plfOld.getFIO().renameTo(ioNew);
     if (!result) {
       throw new IOException();
     }
   } catch (Exception e) {
     throw new JajukException(134, e);
   }
   // OK, remove old file and register this new file
   removeItem(plfOld);
   registerItem(plfNew);
   return plfNew;
 }
 /*
  * (non-Javadoc)
  *
  * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
  */
 public void actionPerformed(final ActionEvent ae) {
   // do not run this in a separate thread because Player actions would die
   // with the thread
   try {
     if (ae.getSource() == jcbHistory) {
       HistoryItem hi;
       hi = History.getInstance().getHistoryItem(jcbHistory.getSelectedIndex());
       if (hi != null) {
         org.jajuk.base.File file = FileManager.getInstance().getFileByID(hi.getFileId());
         if (file != null) {
           try {
             FIFO.getInstance()
                 .push(
                     new StackItem(file, ConfigurationManager.getBoolean(CONF_STATE_REPEAT), true),
                     ConfigurationManager.getBoolean(CONF_OPTIONS_DEFAULT_ACTION_CLICK));
           } catch (JajukException je) {
             // can be thrown if file is null
           }
         } else {
           Messages.showErrorMessage(120);
           jcbHistory.setSelectedItem(null);
         }
       }
     } else if (ae.getSource().equals(jmiNoveltiesModeSong)) {
       ConfigurationManager.setProperty(CONF_NOVELTIES_MODE, MODE_TRACK);
     } else if (ae.getSource().equals(jmiNoveltiesModeAlbum)) {
       ConfigurationManager.setProperty(CONF_NOVELTIES_MODE, MODE_ALBUM);
     } else if (ae.getSource().equals(jmiShuffleModeSong)) {
       ConfigurationManager.setProperty(CONF_GLOBAL_RANDOM_MODE, MODE_TRACK);
     } else if (ae.getSource().equals(jmiShuffleModeAlbum)) {
       ConfigurationManager.setProperty(CONF_GLOBAL_RANDOM_MODE, MODE_ALBUM);
     } else if (ae.getSource().equals(jmiShuffleModeAlbum2)) {
       ConfigurationManager.setProperty(CONF_GLOBAL_RANDOM_MODE, MODE_ALBUM2);
     } else if (ae.getSource().equals(jmiShuffleModeAlbum2)) {
       ConfigurationManager.setProperty(CONF_GLOBAL_RANDOM_MODE, MODE_ALBUM2);
     }
   } catch (Exception e) {
     Log.error(e);
   } finally {
     ObservationManager.notify(new Event(EventSubject.EVENT_PLAYLIST_REFRESH));
   }
 }
 /**
  * Delete a playlist.
  *
  * @param plf DOCUMENT_ME
  */
 public synchronized void removePlaylistFile(Playlist plf) {
   String sFileToDelete =
       plf.getDirectory().getFio().getAbsoluteFile().toString()
           + java.io.File.separatorChar
           + plf.getName();
   java.io.File fileToDelete = new java.io.File(sFileToDelete);
   if (fileToDelete.exists()) {
     if (!fileToDelete.delete()) {
       Log.warn("Could not delete file: " + fileToDelete.toString());
     }
     // check that file has been really deleted (sometimes, we get no
     // exception)
     if (fileToDelete.exists()) {
       Log.error("131", new JajukException(131));
       Messages.showErrorMessage(131);
       return;
     }
   }
   // remove playlist
   removeItem(plf);
 }
 /**
  * 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;
 }