Package org.gudy.azureus2.core3.util

Source Code of org.gudy.azureus2.core3.util.TorrentUtils$torrentAttributeListener

/*
* File    : TorrentUtils.java
* Created : 13-Oct-2003
* By      : stuff
*
* Azureus - a Java Bittorrent client
*
* This program 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.
*
* This program 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 ( see the LICENSE file ).
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

package org.gudy.azureus2.core3.util;

/**
* @author parg
*
*/

import java.io.*;
import java.net.*;
import java.util.*;

import com.aelitis.azureus.core.*;
import com.aelitis.azureus.core.util.CopyOnWriteList;

import org.gudy.azureus2.core3.config.COConfigurationManager;
import org.gudy.azureus2.core3.config.ParameterListener;
import org.gudy.azureus2.core3.internat.*;
import org.gudy.azureus2.core3.logging.LogRelation;
import org.gudy.azureus2.core3.torrent.*;
import org.gudy.azureus2.core3.disk.*;
import org.gudy.azureus2.core3.download.*;


public class
TorrentUtils
{
  public static final int TORRENT_FLAG_LOW_NOISE  = 0x00000001;
 
  private static final String    TORRENT_AZ_PROP_DHT_BACKUP_ENABLE    = "dht_backup_enable";
  private static final String    TORRENT_AZ_PROP_DHT_BACKUP_REQUESTED  = "dht_backup_requested";
  private static final String    TORRENT_AZ_PROP_TORRENT_FLAGS      = "torrent_flags";
  private static final String    TORRENT_AZ_PROP_PLUGINS          = "plugins";
 
  public static final String    TORRENT_AZ_PROP_OBTAINED_FROM      = "obtained_from";
  public static final String    TORRENT_AZ_PROP_PEER_CACHE        = "peer_cache";
  public static final String    TORRENT_AZ_PROP_PEER_CACHE_VALID    = "peer_cache_valid";
 
  private static final String    MEM_ONLY_TORRENT_PATH    = "?/\\!:mem_only:!\\/?";
 
  private static final long    PC_MARKER = RandomUtils.nextLong();
 
  private static final List  created_torrents;
  private static final Set  created_torrents_set;
 
  private static ThreadLocal<Map<String,Object>>    tls  =
    new ThreadLocal<Map<String,Object>>()
    {
      public Map<String,Object>
      initialValue()
      {
        return( new HashMap<String,Object>());
      }
    };
   
  private static volatile Set    ignore_set;
 
  private static boolean bSaveTorrentBackup;
 
  private static CopyOnWriteList  torrent_attribute_listeners = new CopyOnWriteList();
 
  static {
    COConfigurationManager.addAndFireParameterListener("Save Torrent Backup",
        new ParameterListener() {
          public void parameterChanged(String parameterName) {
            bSaveTorrentBackup = COConfigurationManager.getBooleanParameter(parameterName);
          }
        });
   
    created_torrents = COConfigurationManager.getListParameter( "my.created.torrents", new ArrayList());
   
    created_torrents_set  = new HashSet();
   
    Iterator  it = created_torrents.iterator();
   
    while( it.hasNext()){
     
      created_torrents_set.add( new HashWrapper((byte[])it.next()));
    }
  }

  public static TOTorrent
  readFromFile(
    File    file,
    boolean    create_delegate )
   
    throws TOTorrentException
  {
    return( readFromFile( file, create_delegate, false ));
  }
 
    /**
     * If you set "create_delegate" to true then you must understand that this results
     * is piece hashes being discarded and then re-read from the torrent file if needed
     * Therefore, if you delete the original torrent file you're going to get errors
     * if you access the pieces after this (and they've been discarded)
     * @param file
     * @param create_delegate
     * @param force_initial_discard - use to get rid of pieces immediately
     * @return
     * @throws TOTorrentException
     */
 
  public static ExtendedTorrent
  readDelegateFromFile(
    File    file,
    boolean    force_initial_discard )
   
    throws TOTorrentException
  {
    return((ExtendedTorrent)readFromFile( file, true, force_initial_discard ));
  }
 
  public static TOTorrent
  readFromFile(
    File    file,
    boolean    create_delegate,
    boolean    force_initial_discard )
   
    throws TOTorrentException
  {
    TOTorrent torrent;
  
    try{
      torrent = TOTorrentFactory.deserialiseFromBEncodedFile(file);
     
        // make an immediate backup if requested and one doesn't exist
     
        if (bSaveTorrentBackup) {
         
          File torrent_file_bak = new File(file.getParent(), file.getName() + ".bak");

          if ( !torrent_file_bak.exists()){
           
            try{
              torrent.serialiseToBEncodedFile(torrent_file_bak);
             
            }catch( Throwable e ){
             
              Debug.printStackTrace(e);
            }
          }
        }
       
    }catch (TOTorrentException e){
     
      // Debug.outNoStack( e.getMessage() );
     
      File torrentBackup = new File(file.getParent(), file.getName() + ".bak");
     
      if( torrentBackup.exists()){
       
        torrent = TOTorrentFactory.deserialiseFromBEncodedFile(torrentBackup);
       
          // use the original torrent's file name so that when this gets saved
          // it writes back to the original and backups are made as required
          // - set below
      }else{
       
        throw e;
      }
    }
       
    torrent.setAdditionalStringProperty("torrent filename", file.toString());
   
    if ( create_delegate ){
     
      torrentDelegate  res = new torrentDelegate( torrent, file );
     
      if ( force_initial_discard ){
       
        res.discardPieces( SystemTime.getCurrentTime(), true );
      }
     
      return( res );
     
    }else{
     
      return( torrent );
    }
  }

  public static TOTorrent
  readFromBEncodedInputStream(
    InputStream    is )
   
    throws TOTorrentException
  {
    TOTorrent  torrent = TOTorrentFactory.deserialiseFromBEncodedInputStream( is );
   
      // as we've just imported this torrent we want to clear out any possible attributes that we
      // don't want such as "torrent filename"
   
    torrent.removeAdditionalProperties();
   
    return( torrent );
  }
 
  public static void
  setMemoryOnly(
    TOTorrent      torrent,
    boolean        mem_only )
  {
    if ( mem_only ){
     
      torrent.setAdditionalStringProperty("torrent filename", MEM_ONLY_TORRENT_PATH );
     
    }else{
     
      String s = torrent.getAdditionalStringProperty("torrent filename");
     
      if ( s != null && s.equals( MEM_ONLY_TORRENT_PATH )){
       
        torrent.removeAdditionalProperty( "torrent filename" );
      }
    }
  }
 
  public static void
  writeToFile(
    final TOTorrent    torrent )
 
    throws TOTorrentException
  {
    writeToFile( torrent, false );
  }
 
  public static void
  writeToFile(
    TOTorrent    torrent,
    boolean      force_backup )
 
    throws TOTorrentException
  {
     try{
         torrent.getMonitor().enter();
                
        String str = torrent.getAdditionalStringProperty("torrent filename");
       
        if ( str == null ){
         
          throw (new TOTorrentException("TorrentUtils::writeToFile: no 'torrent filename' attribute defined", TOTorrentException.RT_FILE_NOT_FOUND));
        }
       
        if ( str.equals( MEM_ONLY_TORRENT_PATH )){
         
          return;
        }
       
          // save first to temporary file as serialisation may require state to be re-read from
          // the existing file first and if we rename to .bak first then this aint good
               
        File torrent_file_tmp = new File(str + "._az");

        torrent.serialiseToBEncodedFile( torrent_file_tmp );

          // now backup if required
       
        File torrent_file = new File(str);
       
        if (   ( force_backup ||COConfigurationManager.getBooleanParameter("Save Torrent Backup")) &&
            torrent_file.exists()) {
         
          File torrent_file_bak = new File(str + ".bak");
         
          try{
           
              // Will return false if it cannot be deleted (including if the file doesn't exist).
           
            torrent_file_bak.delete();
           
            torrent_file.renameTo(torrent_file_bak);
           
          }catch( SecurityException e){
           
            Debug.printStackTrace( e );
          }
        }
       
          // now rename the temp file to required one
       
        if ( torrent_file.exists()){
         
          torrent_file.delete();
        }
       
        torrent_file_tmp.renameTo( torrent_file );
     
       }finally{
        
         torrent.getMonitor().exit();
       }
  }
 
  public static void
  writeToFile(
    TOTorrent    torrent,
    File      file )
 
    throws TOTorrentException
  {
    writeToFile( torrent, file, false );
  }
 
  public static void
  writeToFile(
    TOTorrent    torrent,
    File      file,
    boolean      force_backup )
 
    throws TOTorrentException
  {   
    torrent.setAdditionalStringProperty("torrent filename", file.toString());
   
    writeToFile( torrent, force_backup );
  }
 
  public static String
  getTorrentFileName(
    TOTorrent    torrent )
 
    throws TOTorrentException
  {
      String str = torrent.getAdditionalStringProperty("torrent filename");
     
      if ( str == null ){
       
        throw( new TOTorrentException("TorrentUtils::getTorrentFileName: no 'torrent filename' attribute defined", TOTorrentException.RT_FILE_NOT_FOUND));
      }

      if ( str.equals( MEM_ONLY_TORRENT_PATH )){
       
        return( null );
      }
     
    return( str );
  }
 
  public static void
  copyToFile(
    TOTorrent    torrent,
    File      file )

    throws TOTorrentException
  {
       torrent.serialiseToBEncodedFile(file);
  }
 
  public static void
  delete(
    TOTorrent     torrent )
 
    throws TOTorrentException
  {
     try{
         torrent.getMonitor().enter();
       
        String str = torrent.getAdditionalStringProperty("torrent filename");
       
        if ( str == null ){
         
          throw( new TOTorrentException("TorrentUtils::delete: no 'torrent filename' attribute defined", TOTorrentException.RT_FILE_NOT_FOUND));
        }
       
        if ( str.equals( MEM_ONLY_TORRENT_PATH )){
         
          return;
        }
       
        if ( !new File(str).delete()){
         
          throw( new TOTorrentException("TorrentUtils::delete: failed to delete '" + str + "'", TOTorrentException.RT_WRITE_FAILS));
        }
   
        new File( str + ".bak" ).delete();
       
      }finally{
       
        torrent.getMonitor().exit();
      }
  }
 
  public static void
  delete(
    File     torrent_file,
    boolean    force_no_recycle )
  {
    if ( !FileUtil.deleteWithRecycle( torrent_file, force_no_recycle )){
     
        Debug.out( "TorrentUtils::delete: failed to delete '" + torrent_file + "'" );
      }
 
      new File( torrent_file.toString() + ".bak" ).delete();
  }
 
  public static boolean
  move(
    File    from_torrent,
    File    to_torrent )
  {
    if ( !FileUtil.renameFile(from_torrent, to_torrent )){
     
      return( false );
    }
   
    if ( new File( from_torrent.toString() + ".bak").exists()){
     
      FileUtil.renameFile(
        new File( from_torrent.toString() + ".bak"),
        new File( to_torrent.toString() + ".bak"));
    }
   
    return( true );
  }
   
  public static String
  exceptionToText(
    TOTorrentException  e )
  {
    String  errorDetail;
   
    int  reason = e.getReason();
           
    if ( reason == TOTorrentException.RT_FILE_NOT_FOUND ){
                          
      errorDetail = MessageText.getString("DownloadManager.error.filenotfound" );
                 
    }else if ( reason == TOTorrentException.RT_ZERO_LENGTH ){
      
      errorDetail = MessageText.getString("DownloadManager.error.fileempty");
               
    }else if ( reason == TOTorrentException.RT_TOO_BIG ){
             
      errorDetail = MessageText.getString("DownloadManager.error.filetoobig");
             
    }else if ( reason == TOTorrentException.RT_DECODE_FAILS ){
  
      errorDetail = MessageText.getString("DownloadManager.error.filewithouttorrentinfo" );
              
    }else if ( reason == TOTorrentException.RT_UNSUPPORTED_ENCODING ){
             
      errorDetail = MessageText.getString("DownloadManager.error.unsupportedencoding");
             
    }else if ( reason == TOTorrentException.RT_READ_FAILS ){
 
      errorDetail = MessageText.getString("DownloadManager.error.ioerror");
         
    }else if ( reason == TOTorrentException.RT_HASH_FAILS ){
     
      errorDetail = MessageText.getString("DownloadManager.error.sha1");
         
    }else if ( reason == TOTorrentException.RT_CANCELLED ){
     
      errorDetail = MessageText.getString("DownloadManager.error.operationcancancelled");
           
    }else{
         
      errorDetail = Debug.getNestedExceptionMessage(e);
    }
         
    String  msg = Debug.getNestedExceptionMessage(e);
       
    if ( errorDetail.indexOf( msg ) == -1){
       
      errorDetail += " (" + msg + ")";
    }
   
    return( errorDetail );
  }
 
  public static List<List<String>>
  announceGroupsToList(
    TOTorrent  torrent )
  {
    List<List<String>>  groups = new ArrayList<List<String>>();
   
    TOTorrentAnnounceURLGroup group = torrent.getAnnounceURLGroup();
   
    TOTorrentAnnounceURLSet[]  sets = group.getAnnounceURLSets();
   
    if ( sets.length == 0 ){
   
      List<String>  s = new ArrayList<String>();
     
      s.add( torrent.getAnnounceURL().toString());
     
      groups.add(s);
    }else{
     
      for (int i=0;i<sets.length;i++){
     
        List<String>  s = new ArrayList<String>();
               
        TOTorrentAnnounceURLSet  set = sets[i];
       
        URL[]  urls = set.getAnnounceURLs();
       
        for (int j=0;j<urls.length;j++){
       
          s.add( urls[j].toString());
        }
       
        if ( s.size() > 0 ){
         
          groups.add(s);
        }
      }
    }
   
    return( groups );
  }
 
  public static void
  listToAnnounceGroups(
    List<List<String>>    groups,
    TOTorrent        torrent )
  {
    try{
      TOTorrentAnnounceURLGroup tg = torrent.getAnnounceURLGroup();
     
      if ( groups.size() == 1 ){
       
        List  set = (List)groups.get(0);
       
        if ( set.size() == 1 ){
         
          torrent.setAnnounceURL( new URL((String)set.get(0)));
         
          tg.setAnnounceURLSets( new TOTorrentAnnounceURLSet[0]);
         
          return;
        }
      }
     
     
      Vector  g = new Vector();
     
      for (int i=0;i<groups.size();i++){
       
        List  set = (List)groups.get(i);
       
        URL[]  urls = new URL[set.size()];
       
        for (int j=0;j<set.size();j++){
       
          urls[j] = new URL((String)set.get(j));
        }
       
        if ( urls.length > 0 ){
         
          g.add( tg.createAnnounceURLSet( urls ));
        }
      }
     
      TOTorrentAnnounceURLSet[]  sets = new TOTorrentAnnounceURLSet[g.size()];
     
      g.copyInto( sets );
     
      tg.setAnnounceURLSets( sets );
     
      if ( sets.length == 0 ){
     
          // hmm, no valid urls at all
       
        torrent.setAnnounceURL( new URL( "http://no.valid.urls.defined/announce"));
      }
     
    }catch( MalformedURLException e ){
     
      Debug.printStackTrace( e );
    }
  }
 
  public static void
  announceGroupsInsertFirst(
    TOTorrent  torrent,
    String    first_url )
  {
    try{
     
      announceGroupsInsertFirst( torrent, new URL( first_url ));
     
    }catch( MalformedURLException e ){
     
      Debug.printStackTrace( e );
    }
  }
 
  public static void
  announceGroupsInsertFirst(
    TOTorrent  torrent,
    URL      first_url )
  {
    announceGroupsInsertFirst( torrent, new URL[]{ first_url });
  }
 
  public static void
  announceGroupsInsertFirst(
    TOTorrent  torrent,
    URL[]    first_urls )
  {
    TOTorrentAnnounceURLGroup group = torrent.getAnnounceURLGroup();
   
    TOTorrentAnnounceURLSet[] sets = group.getAnnounceURLSets();

    TOTorrentAnnounceURLSet set1 = group.createAnnounceURLSet( first_urls );
   
   
    if ( sets.length > 0 ){
     
      TOTorrentAnnounceURLSet[]  new_sets = new TOTorrentAnnounceURLSet[sets.length+1];
     
      new_sets[0] = set1;
     
      System.arraycopy( sets, 0, new_sets, 1, sets.length );
     
      group.setAnnounceURLSets( new_sets );
         
    }else{
     
      TOTorrentAnnounceURLSet set2 = group.createAnnounceURLSet(new URL[]{torrent.getAnnounceURL()});
     
      group.setAnnounceURLSets(
        new  TOTorrentAnnounceURLSet[]{ set1, set2 });
    }
  }
 
  public static void
  announceGroupsInsertLast(
    TOTorrent  torrent,
    URL[]    first_urls )
  {
    TOTorrentAnnounceURLGroup group = torrent.getAnnounceURLGroup();
   
    TOTorrentAnnounceURLSet[] sets = group.getAnnounceURLSets();

    TOTorrentAnnounceURLSet set1 = group.createAnnounceURLSet( first_urls );
   
   
    if ( sets.length > 0 ){
     
      TOTorrentAnnounceURLSet[]  new_sets = new TOTorrentAnnounceURLSet[sets.length+1];
     
      new_sets[sets.length] = set1;
     
      System.arraycopy( sets, 0, new_sets, 0, sets.length );
     
      group.setAnnounceURLSets( new_sets );
         
    }else{
     
      TOTorrentAnnounceURLSet set2 = group.createAnnounceURLSet(new URL[]{torrent.getAnnounceURL()});
     
      group.setAnnounceURLSets(
        new  TOTorrentAnnounceURLSet[]{ set2, set1 });
    }
  }
   
  public static void
  announceGroupsSetFirst(
    TOTorrent  torrent,
    String    first_url )
  {
    List  groups = announceGroupsToList( torrent );
   
    boolean  found = false;
 
    outer:
    for (int i=0;i<groups.size();i++){
     
      List  set = (List)groups.get(i);
     
      for (int j=0;j<set.size();j++){
   
        if ( first_url.equals(set.get(j))){
     
          set.remove(j);
         
          set.add(0, first_url);
         
          groups.remove(set);
         
          groups.add(0,set);
 
          found = true;
         
          break outer;
        }
      }
    }
   
    if ( !found ){
     
      System.out.println( "TorrentUtils::announceGroupsSetFirst - failed to find '" + first_url + "'" );
    }
   
    listToAnnounceGroups( groups, torrent );
  }
 
  public static boolean
  announceGroupsContainsURL(
    TOTorrent  torrent,
    String    url )
  {
    List  groups = announceGroupsToList( torrent );
   
    for (int i=0;i<groups.size();i++){
     
      List  set = (List)groups.get(i);
     
      for (int j=0;j<set.size();j++){
   
        if ( url.equals(set.get(j))){
     
          return( true );
        }
      }
    }
   
    return( false );
  }
 
  public static boolean
  mergeAnnounceURLs(
    TOTorrent   new_torrent,
    TOTorrent  dest_torrent )
  {
    if ( new_torrent == null || dest_torrent == null ){
     
      return( false);
    }
   
    List  new_groups   = announceGroupsToList( new_torrent );
    List   dest_groups = announceGroupsToList( dest_torrent );
   
    List  groups_to_add = new ArrayList();
   
    for (int i=0;i<new_groups.size();i++){
     
      List new_set = (List)new_groups.get(i);
     
      boolean  match = false;
     
      for (int j=0;j<dest_groups.size();j++){
       
        List dest_set = (List)dest_groups.get(j);
       
        boolean same = new_set.size() == dest_set.size();
       
        if ( same ){
         
          for (int k=0;k<new_set.size();k++){
           
            String new_url = (String)new_set.get(k);
           
            if ( !dest_set.contains(new_url)){
             
              same = false;
             
              break;
            }
          }
        }
       
        if ( same ){
         
          match = true;
         
          break;
        }
      }
     
      if ( !match ){
   
        groups_to_add.add( new_set );
      }
    }
   
    if ( groups_to_add.size() == 0 ){
     
      return( false );
    }
   
    for (int i=0;i<groups_to_add.size();i++){
     
      dest_groups.add(i,groups_to_add.get(i));
    }
   
    listToAnnounceGroups( dest_groups, dest_torrent );
   
    return( true );
  }
 
  public static boolean
  replaceAnnounceURL(
    TOTorrent    torrent,
    URL        old_url,
    URL        new_url )
  {
    boolean  found = false;
   
    String  old_str = old_url.toString();
    String  new_str = new_url.toString();
   
    List  l = announceGroupsToList( torrent );
   
    for (int i=0;i<l.size();i++){
     
      List  set = (List)l.get(i);
     
      for (int j=0;j<set.size();j++){
   
        if (((String)set.get(j)).equals(old_str)){
         
          found  = true;
         
          set.set( j, new_str );
        }
      }
    }
   
    if ( found ){
     
      listToAnnounceGroups( l, torrent );
    }
   
    if ( torrent.getAnnounceURL().toString().equals( old_str )){
     
      torrent.setAnnounceURL( new_url );
     
      found  = true;
    }
   
    if ( found ){
     
      try{
        writeToFile( torrent );
       
      }catch( Throwable e ){
       
        Debug.printStackTrace(e);
       
        return( false );
      }
    }
   
    return( found );
  }
 
  public static void
  setResumeDataCompletelyValid(
    DownloadManagerState  download_manager_state )
  {
    DiskManagerFactory.setResumeDataCompletelyValid( download_manager_state );
  }
 
  public static String
  getLocalisedName(
    TOTorrent    torrent )
  {
    if (torrent == null) {
      return "";
    }
    try{
      String utf8Name = torrent.getUTF8Name();
      if (utf8Name != null) {
        return utf8Name;
      }
     
      LocaleUtilDecoder decoder = LocaleTorrentUtil.getTorrentEncodingIfAvailable( torrent );
     
      if ( decoder == null ){
       
        return( new String(torrent.getName(),Constants.DEFAULT_ENCODING));
      }
     
      return( decoder.decodeString(torrent.getName()));
     
    }catch( Throwable e ){
     
      Debug.printStackTrace( e );
     
      return( new String( torrent.getName()));
    }
  }
 
  public static void
  setTLSTorrentHash(
    HashWrapper    hash )
  {
    tls.get().put( "hash", hash );
  }
 
  public static TOTorrent
  getTLSTorrent()
  {
    HashWrapper  hash = (HashWrapper)(tls.get()).get("hash");
   
    if ( hash != null ){
     
      try{
        AzureusCore  core = AzureusCoreFactory.getSingleton();
       
        DownloadManager dm = core.getGlobalManager().getDownloadManager( hash );
       
        if ( dm != null ){
             
          return( dm.getTorrent());
        }
      }catch( Throwable e ){
       
        Debug.printStackTrace(e);
      }
    }
   
    return( null );
  }
 
  public static void
  setTLSDescription(
    String    desc )
  {
    tls.get().put( "desc", desc );
  }
 
  public static String
  getTLSDescription()
  {
    return((String)tls.get().get( "desc" ));
  }
 
    /**
     * get tls for cloning onto another thread
     * @return
     */
 
  public static Object
  getTLS()
  {
    return( new HashMap<String,Object>(tls.get()));
  }
 
  public static void
  setTLS(
    Object  obj )
  {
    Map<String,Object>  m = (Map<String,Object>)obj;
   
    Map<String,Object> tls_map = tls.get();
   
    tls_map.clear();
   
    tls_map.putAll(m);
  }
 
  public static URL
  getDecentralisedEmptyURL()
  {
    try{
      return( new URL( "dht://" ));
     
    }catch( Throwable e ){
     
      Debug.printStackTrace(e);
     
      return( null );
    }
  }
 
  public static URL
  getDecentralisedURL(
    TOTorrent  torrent )
  {
    try{
      return( new URL( "dht://" + ByteFormatter.encodeString( torrent.getHash()) + ".dht/announce" ));
     
    }catch( Throwable e ){
     
      Debug.out( e );
     
      return( getDecentralisedEmptyURL());
    }
  }

  public static void
  setDecentralised(
    TOTorrent  torrent )
  {
       torrent.setAnnounceURL( getDecentralisedURL( torrent ));
  }
   
  public static boolean
  isDecentralised(
    TOTorrent    torrent )
  {
    if ( torrent == null ){
     
      return( false );
    }
   
    return( isDecentralised( torrent.getAnnounceURL()));
  }
 

  public static boolean
  isDecentralised(
    URL    url )
  {
    if ( url == null ){
     
      return( false );
    }
   
    return( url.getProtocol().equalsIgnoreCase( "dht" ));
  }
 
  private static Map
  getAzureusProperties(
    TOTorrent  torrent )
  {
    Map  m = torrent.getAdditionalMapProperty( TOTorrent.AZUREUS_PROPERTIES );
   
    if ( m == null ){
     
      m = new HashMap();
     
      torrent.setAdditionalMapProperty( TOTorrent.AZUREUS_PROPERTIES, m );
    }
   
    return( m );
  }
 
  private static Map
  getAzureusPrivateProperties(
    TOTorrent  torrent )
  {
    Map  m = torrent.getAdditionalMapProperty( TOTorrent.AZUREUS_PRIVATE_PROPERTIES );
   
    if ( m == null ){
     
      m = new HashMap();
     
      torrent.setAdditionalMapProperty( TOTorrent.AZUREUS_PRIVATE_PROPERTIES, m );
    }
   
    return( m );
  }
 
  public static void
  setObtainedFrom(
    File      file,
    String      str )
  {
    try{
      TOTorrent  torrent = readFromFile( file, false, false );
     
      setObtainedFrom( torrent, str );
     
      writeToFile( torrent );
     
    } catch (TOTorrentException e) {
      // ignore, file probably not torrent
     
    }catch( Throwable e ){
     
      Debug.out( e );
    }
  }
 
  public static void
  setObtainedFrom(
    TOTorrent    torrent,
    String      str )
  {
    Map  m = getAzureusPrivateProperties( torrent );
     
    try{
      m.put( TORRENT_AZ_PROP_OBTAINED_FROM, str.getBytes( "UTF-8" ));
     
      fireAttributeListener( torrent, TORRENT_AZ_PROP_OBTAINED_FROM, str );
     
    }catch( Throwable e ){
     
      Debug.printStackTrace(e);
    }
  }
 
  public static String
  getObtainedFrom(
    TOTorrent    torrent )
  {
    Map  m = getAzureusPrivateProperties( torrent );

    byte[]  from = (byte[])m.get( TORRENT_AZ_PROP_OBTAINED_FROM );
   
    if ( from != null ){
     
      try{
        return( new String( from, "UTF-8" ));
       
      }catch( Throwable e ){
     
        Debug.printStackTrace(e);
      }
    }
   
    return( null );
  }
 
  public static void
  setPeerCache(
    TOTorrent    torrent,
    Map        pc )
  {
    Map  m = getAzureusPrivateProperties( torrent );
     
    try{
      m.put( TORRENT_AZ_PROP_PEER_CACHE, pc );
           
    }catch( Throwable e ){
     
      Debug.printStackTrace(e);
    }
  }
 
  public static void
  setPeerCacheValid(
    TOTorrent    torrent )
  {
    Map  m = getAzureusPrivateProperties( torrent );
     
    try{
      m.put( TORRENT_AZ_PROP_PEER_CACHE_VALID, new Long( PC_MARKER ));
           
    }catch( Throwable e ){
     
      Debug.printStackTrace(e);
    }
  }
 
  public static Map
  getPeerCache(
    TOTorrent    torrent )
  {
    try{
      Map  m = getAzureusPrivateProperties( torrent );
 
      Long value = (Long)m.get( TORRENT_AZ_PROP_PEER_CACHE_VALID );
     
      if ( value != null && value == PC_MARKER ){
     
        Map  pc = (Map)m.get( TORRENT_AZ_PROP_PEER_CACHE );
 
        return( pc );
      }
     
    }catch( Throwable e ){
     
      Debug.out( e );
    }
   
    return( null );
  }
 
  public static void
  setFlag(
    TOTorrent    torrent,
    int        flag,
    boolean      value )
  {
    Map  m = getAzureusProperties( torrent );
   
    Long  flags = (Long)m.get( TORRENT_AZ_PROP_TORRENT_FLAGS );
   
    if ( flags == null ){
     
      flags = new Long(0);
    }   
   
    m.put( TORRENT_AZ_PROP_TORRENT_FLAGS, new Long(flags.intValue() | flag ));
  }
   
  public static boolean
  getFlag(
    TOTorrent    torrent,
    int        flag )
  {
    Map  m = getAzureusProperties( torrent );
   
    Long  flags = (Long)m.get( TORRENT_AZ_PROP_TORRENT_FLAGS );
   
    if ( flags == null ){
     
      return( false );
    }

    return(( flags.intValue() & flag ) != 0 );
  }
 
  public static void
  setPluginStringProperty(
    TOTorrent    torrent,
    String      name,
    String      value )
  {
    Map  m = getAzureusProperties( torrent );
   
    Object obj = m.get( TORRENT_AZ_PROP_PLUGINS );
   
    Map  p;
   
    if ( obj instanceof Map ){
     
      p = (Map)obj;
     
    }else{
     
      p = new HashMap();
     
      m.put( TORRENT_AZ_PROP_PLUGINS, p );
    }
   
    if ( value == null ){
     
      p.remove( name );
     
    }else{
     
      p.put( name, value.getBytes());
    }
  }
 
  public static String
  getPluginStringProperty(
    TOTorrent    torrent,
    String      name )
  {
    Map  m = getAzureusProperties( torrent );
   
    Object  obj = m.get( TORRENT_AZ_PROP_PLUGINS );
   
    if ( obj instanceof Map ){
   
      Map p = (Map)obj;
     
      obj = p.get( name );
     
      if ( obj instanceof byte[]){
     
        return( new String((byte[])obj));
      }
    }
   
    return( null );
  }
 
  public static void
  setPluginMapProperty(
    TOTorrent    torrent,
    String      name,
    Map        value )
  {
    Map  m = getAzureusProperties( torrent );
   
    Object obj = m.get( TORRENT_AZ_PROP_PLUGINS );
   
    Map  p;
   
    if ( obj instanceof Map ){
     
      p = (Map)obj;
     
    }else{
     
      p = new HashMap();
     
      m.put( TORRENT_AZ_PROP_PLUGINS, p );
    }
   
    if ( value == null ){
     
      p.remove( name );
     
    }else{
     
      p.put( name, value );
    }
  }
 
  public static Map
  getPluginMapProperty(
    TOTorrent    torrent,
    String      name )
  {
    Map  m = getAzureusProperties( torrent );
   
    Object  obj = m.get( TORRENT_AZ_PROP_PLUGINS );
   
    if ( obj instanceof Map ){
   
      Map p = (Map)obj;
     
      obj = p.get( name );
     
      if ( obj instanceof Map ){
   
        return((Map)obj);
      }
    }
   
    return( null );
  }
 
  public static void
  setDHTBackupEnabled(
    TOTorrent    torrent,
    boolean      enabled )
  {
    Map  m = getAzureusProperties( torrent );
   
    m.put( TORRENT_AZ_PROP_DHT_BACKUP_ENABLE, new Long(enabled?1:0));
  }
 
  public static boolean
  getDHTBackupEnabled(
    TOTorrent  torrent )
  {
      // missing -> true
   
    Map  m = getAzureusProperties( torrent );
   
    Object  obj = m.get( TORRENT_AZ_PROP_DHT_BACKUP_ENABLE );
   
    if ( obj instanceof Long ){
   
      return( ((Long)obj).longValue() == 1 );
    }
   
    return( true );
  }
 
  public static boolean
  isDHTBackupRequested(
    TOTorrent  torrent )
  {
      // missing -> false
   
    Map  m = getAzureusProperties( torrent );
   
    Object obj = m.get( TORRENT_AZ_PROP_DHT_BACKUP_REQUESTED );
   
    if ( obj instanceof Long ){
   
      return( ((Long)obj).longValue() == 1 );
    }
   
    return( false );
  }
 
  public static void
  setDHTBackupRequested(
    TOTorrent    torrent,
    boolean      requested )
  {
    Map  m = getAzureusProperties( torrent );
   
    m.put( TORRENT_AZ_PROP_DHT_BACKUP_REQUESTED, new Long(requested?1:0));
  }
   
 
  public static boolean isReallyPrivate(TOTorrent torrent) {
    if ( torrent == null ){
     
      return( false );
   
   
    if ( UrlUtils.containsPasskey( torrent.getAnnounceURL())){
       
      return torrent.getPrivate();
    }
   
    return false;
  }
 
  public static boolean
  getPrivate(
    TOTorrent    torrent )
  {
    if ( torrent == null ){
     
      return( false );
   
     
    return( torrent.getPrivate());
  }
 
  public static void
  setPrivate(
    TOTorrent    torrent,
    boolean      _private )
  {
    if ( torrent == null ){
     
      return;
    }
   
    try{
      torrent.setPrivate( _private );
     
    }catch( Throwable e ){
     
      Debug.printStackTrace(e);
    }
  }
 
  public static Set
  getIgnoreSet()
  {
    return(getIgnoreSetSupport(false));
  }
 
  public static synchronized Set
  getIgnoreSetSupport(
    boolean  force )
  {
    if ( ignore_set == null || force ){
     
      Set    new_ignore_set  = new HashSet();
       
      String  ignore_list = COConfigurationManager.getStringParameter( "File.Torrent.IgnoreFiles", TOTorrent.DEFAULT_IGNORE_FILES );
     
      if ( ignore_set == null ){
       
          // first time - add the listener
       
        COConfigurationManager.addParameterListener(
          "File.Torrent.IgnoreFiles",
          new ParameterListener()
          {
            public void
            parameterChanged(
              String parameterName)
            {
              getIgnoreSetSupport( true );
            }
          });
      }
     
      int  pos = 0;
     
      while(true){
       
        int  p1 = ignore_list.indexOf( ";", pos );
       
        String  bit;
       
        if ( p1 == -1 ){
         
          bit = ignore_list.substring(pos);
         
        }else{
         
          bit  = ignore_list.substring( pos, p1 );
         
          pos  = p1+1;
        }
       
        new_ignore_set.add(bit.trim().toLowerCase());
       
        if ( p1 == -1 ){
         
          break;
        }
      }
     
      ignore_set = new_ignore_set;
    }
   
    return( ignore_set );
  }
 
 
 
    // this class exists to minimise memory requirements by discarding the piece hash values
    // when "idle"
 
  private static final int  PIECE_HASH_TIMEOUT  = 3*60*1000;
 
  private static Map  torrent_delegates = new WeakHashMap();
 
  static{
    SimpleTimer.addPeriodicEvent(
      "TorrentUtils:pieceDiscard",
      PIECE_HASH_TIMEOUT/2,
      new TimerEventPerformer()
      {
        public void
        perform(
          TimerEvent  event )
        {
          long  now = SystemTime.getCurrentTime();
         
          synchronized( torrent_delegates ){
           
            Iterator it = torrent_delegates.keySet().iterator();
           
            while( it.hasNext()){
             
              ((torrentDelegate)it.next()).discardPieces(now,false);
            }
          }
        }
      });
  }
 
  private static HashSet  torrentFluffKeyset = new HashSet(2);
  private static Map    fluffThombstone = new HashMap(1);
 
  /**
   * Register keys that are used for heavyweight maps that should be discarded when the torrent is not in use
   * Make sure these keys are only ever used for Map objects!
   */
  public static void
  registerMapFluff(
    String[]    fluff )
  {
    synchronized (TorrentUtils.class)
    {
      for (int i = 0; i < fluff.length; i++)
        torrentFluffKeyset.add(fluff[i]);
    }
  }
 
  public interface
  ExtendedTorrent
    extends TOTorrent
  {
    public byte[][]
    peekPieces()
           
      throws TOTorrentException;
   
    public void
    setDiscardFluff(
      boolean  discard );
  }
 
  private static class
  torrentDelegate
    extends LogRelation
    implements ExtendedTorrent
  {
    private TOTorrent    delegate;
    private File      file;
   
    private boolean      fluff_dirty;
   
    private long      last_pieces_read_time  = SystemTime.getCurrentTime();
   
    protected
    torrentDelegate(
      TOTorrent    _delegate,
      File      _file )
    {
      delegate    = _delegate;
      file      = _file;
     
      synchronized( torrent_delegates ){
       
        torrent_delegates.put( this, null );
      }
    }
   
    public void
    setDiscardFluff(
      boolean  discard )
    {
      if ( discard && !torrentFluffKeyset.isEmpty() ){
       
        //System.out.println( "Discarded fluff for " + new String(getName()));
       
        try{
             getMonitor().enter();
         
             try{
                 // if file is out of sync with fluff then force a write
              
               if ( fluff_dirty ){
                
                 boolean[]  restored = restoreState( true, true );
                
                 delegate.serialiseToBEncodedFile( file );
                
                 fluff_dirty = false;
                
                 if ( restored[0] ){
                  
                   discardPieces( SystemTime.getCurrentTime(), true );
                 }
               }
              
               for(Iterator it = torrentFluffKeyset.iterator();it.hasNext();){
                
                 delegate.setAdditionalMapProperty( (String)it.next(), fluffThombstone );
               }
             }catch( Throwable e ){
            
               Debug.printStackTrace( e );
             }
        }finally{
         
          getMonitor().exit();
        }
      }
    }
   
    public byte[]
    getName()
    {
      return( delegate.getName());
    }
       
    public boolean
    isSimpleTorrent()
    {
      return( delegate.isSimpleTorrent());
    }
       
    public byte[]
    getComment()
    {
      return( delegate.getComment());   
    }

    public void
    setComment(
      String    comment )
    {
      delegate.setComment( comment );
    }
       
    public long
    getCreationDate()
    {
      return( delegate.getCreationDate());
    }
   
    public void
    setCreationDate(
      long    date )
    {
      delegate.setCreationDate( date );
    }
   
    public byte[]
    getCreatedBy()
    {
      return( delegate.getCreatedBy());
    }
   
     public void
    setCreatedBy(
      byte[]    cb )
       {
        delegate.setCreatedBy( cb );
       }
    
    public boolean
    isCreated()
    {
      return( delegate.isCreated());
    }
   
    public URL
    getAnnounceURL()
    {
      return( delegate.getAnnounceURL());
    }

    public boolean
    setAnnounceURL(
      URL    url )
    {
      return delegate.setAnnounceURL( url );
    }
     
   
    public TOTorrentAnnounceURLGroup
    getAnnounceURLGroup()
    {
      return( delegate.getAnnounceURLGroup());
    }
    
    protected void
    discardPieces(
      long    now,
      boolean    force )
    {
        // handle clock changes backwards
     
      if ( now < last_pieces_read_time && !force ){
       
        last_pieces_read_time  = now;
       
      }else{
     
        try{
          if(    ( now - last_pieces_read_time > PIECE_HASH_TIMEOUT || force ) &&
              delegate.getPieces() != null ){
           
            try{
              getMonitor().enter();
             
              // System.out.println( "clearing pieces for '" + new String(getName()) + "'");

              delegate.setPieces( null );
            }finally{
             
              getMonitor().exit();
            }
          }
        }catch( Throwable e ){
         
          Debug.printStackTrace(e);
        }
      }
    }
   
    public byte[][]
    getPieces()
   
      throws TOTorrentException
    {
      byte[][]  res = delegate.getPieces();
     
      last_pieces_read_time  = SystemTime.getCurrentTime();
   
      if ( res == null ){
            
        // System.out.println( "recovering pieces for '" + new String(getName()) + "'");
       
        try{
             getMonitor().enter();

             restoreState( true, false );
            
             res = delegate.getPieces();
            
        }finally{
         
          getMonitor().exit();
        }
      }
     
      return( res );
    }

    /**
     * monitor must be held before calling me
     * @param do_pieces
     * @param do_fluff
     * @throws TOTorrentException
     */
   
    protected boolean[]
    restoreState(
      boolean    do_pieces,
      boolean    do_fluff )
   
      throws TOTorrentException
    {
         boolean  had_pieces = delegate.getPieces() != null;
        
         boolean  had_fluff = true;
        
         for(Iterator it = torrentFluffKeyset.iterator();it.hasNext();){
          
           had_fluff &= delegate.getAdditionalMapProperty( (String)it.next() ) != fluffThombstone;
         }

         if ( had_pieces ){
          
           do_pieces = false;
         }
        
         if ( had_fluff ){
          
           do_fluff = false;
         }
        
         if ( do_pieces || do_fluff ){
        
           TOTorrent  temp = readFromFile( file, false );
       
           if ( do_pieces ){
          
             byte[][] res  = temp.getPieces();
       
             delegate.setPieces( res );
           }
          
           if ( do_fluff ){
            
             for (Iterator it = torrentFluffKeyset.iterator(); it.hasNext();){
         
            String fluffKey = (String) it.next();
           
              // only update the discarded entries as non-discarded may be out of sync
              // with the file contents
           
            if ( delegate.getAdditionalMapProperty( fluffKey ) == fluffThombstone ){
             
              delegate.setAdditionalMapProperty(fluffKey, temp.getAdditionalMapProperty(fluffKey));
            }
          }
           }
         }
        
         return( new boolean[]{ do_pieces, do_fluff });
    }
   
      /**
       * peeks the pieces, will return null if they are discarded
       * @return
       */
   
    public byte[][]
    peekPieces()
   
      throws TOTorrentException
    {
      return( delegate.getPieces());
    }
   
    public void
    setPieces(
      byte[][]  pieces )
   
      throws TOTorrentException
    {
      throw( new TOTorrentException( "Unsupported Operation", TOTorrentException.RT_WRITE_FAILS ));
    }
   
    public long
    getPieceLength()
    {
      return( delegate.getPieceLength());
    }

    public int
    getNumberOfPieces()
    {
      return( delegate.getNumberOfPieces());
    }
   
    public long
    getSize()
    {
      return( delegate.getSize());
    }
   
    public TOTorrentFile[]
    getFiles()
    {
      return( delegate.getFiles());
    }
        
    public byte[]
    getHash()
         
      throws TOTorrentException
    {
      return( delegate.getHash());
    }
   
    public HashWrapper
    getHashWrapper()
         
      throws TOTorrentException
    {
      return( delegate.getHashWrapper());
    }
   
       public void
      setHashOverride(
        byte[] hash )
     
        throws TOTorrentException
      {
        throw( new TOTorrentException( "Not supported", TOTorrentException.RT_HASH_FAILS ));
      }
      
    public boolean
    getPrivate()
    {
      return( delegate.getPrivate());
    }
   
    public void
    setPrivate(
      boolean  _private )
   
      throws TOTorrentException
    {
        // don't support this as it changes teh torrent hash
     
      throw( new TOTorrentException( "Can't amend private attribute", TOTorrentException.RT_WRITE_FAILS ));
    }
   
    public boolean
    hasSameHashAs(
      TOTorrent    other )
    {
      return( delegate.hasSameHashAs( other ));
    }
       
    public void
    setAdditionalStringProperty(
      String    name,
      String    value )
    {
      delegate.setAdditionalStringProperty( name, value );
    }
     
    public String
    getAdditionalStringProperty(
      String    name )
    {
      return( delegate.getAdditionalStringProperty( name ));
    }
     
    public void
    setAdditionalByteArrayProperty(
      String    name,
      byte[]    value )
    {
      delegate.setAdditionalByteArrayProperty( name, value );
    }
     
    public byte[]
    getAdditionalByteArrayProperty(
      String    name )
    {
      return( delegate.getAdditionalByteArrayProperty( name ));
    }
   
    public void
    setAdditionalLongProperty(
      String    name,
      Long    value )
    {
      delegate.setAdditionalLongProperty( name, value );
    }
     
    public Long
    getAdditionalLongProperty(
      String    name )
    {
      return( delegate.getAdditionalLongProperty( name ));
    }
     
   
    public void
    setAdditionalListProperty(
      String    name,
      List    value )
    {
      delegate.setAdditionalListProperty( name, value );
    }
     
    public List
    getAdditionalListProperty(
      String    name )
    {
      return( delegate.getAdditionalListProperty( name ));
    }
     
    public void
    setAdditionalMapProperty(
      String    name,
      Map      value )
    {
      if ( torrentFluffKeyset.contains(name)){

        //System.out.println( "Set fluff for " + new String(getName()) + " to " + value );

        try{
          getMonitor().enter();

          delegate.setAdditionalMapProperty( name, value );
         
          fluff_dirty = true;
         
        }finally{
         
          getMonitor().exit();
        }
      }else{
       
        delegate.setAdditionalMapProperty( name, value )
      }
    }
     
    public Map
    getAdditionalMapProperty(
      String    name )
    {
      if (torrentFluffKeyset.contains(name)){
       
        try{
          getMonitor().enter();

          Map  result = delegate.getAdditionalMapProperty( name );
         
          if ( result == fluffThombstone ){
         
            try{
              restoreState( false, true );
             
              Map res = delegate.getAdditionalMapProperty( name );

              //System.out.println( "Restored fluff for " + new String(getName()) + " to " + res );

              return( res );
             
            }catch( Throwable e ){
             
              Debug.out( "Property '" + name + " lost due to torrent read error", e );
            }
          }
        }finally{
         
          getMonitor().exit();
        }
      }
     
      return( delegate.getAdditionalMapProperty( name ));
    }
   
    public Object getAdditionalProperty(String name) {
      if (torrentFluffKeyset.contains(name))
      {
        try
        {
          getMonitor().enter();
         
          Object result = delegate.getAdditionalProperty(name);
          if (result == fluffThombstone)
          {
            try
            {
              restoreState(false, true);
              Object res = delegate.getAdditionalProperty(name);
              //System.out.println( "Restored fluff for " + new String(getName()) + " to " + res );
             
              return (res);
             
            } catch (Throwable e)
            {
              Debug.out("Property '" + name + " lost due to torrent read error", e);
            }
          }
        } finally
        {
          getMonitor().exit();
        }
      }
     
      return delegate.getAdditionalProperty(name);
    }

    public void
    setAdditionalProperty(
      String    name,
      Object    value )
    {
      if ( torrentFluffKeyset.contains(name)){

        //System.out.println( "Set fluff for " + new String(getName()) + " to " + value );

        try{
          getMonitor().enter();

          delegate.setAdditionalProperty( name, value );
         
          fluff_dirty = true;
         
        }finally{
         
          getMonitor().exit();
        }
      }else{
     
        delegate.setAdditionalProperty( name, value );
      }
    }
   
    public void
    removeAdditionalProperty(
      String name )
    {
      if(delegate.getAdditionalProperty(name) == null)
        return;
     
      if ( torrentFluffKeyset.contains(name)){

        //System.out.println( "Set fluff for " + new String(getName()) + " to " + value );

        try{
          getMonitor().enter();
         
          delegate.removeAdditionalProperty( name );
         
          fluff_dirty = true;
         
        }finally{
         
          getMonitor().exit();
        }
      }else{
       
        delegate.removeAdditionalProperty( name );
      }
    } 
   
    public void
    removeAdditionalProperties()
    {
      try{
        getMonitor().enter();

        delegate.removeAdditionalProperties();
       
        fluff_dirty = true;
       
      }finally{
       
        getMonitor().exit();
      }
    }   

    public void
    serialiseToBEncodedFile(
      File    target_file )
       
      throws TOTorrentException
    {
        // make sure pieces are current
     
      try{
           getMonitor().enter();
                     
           boolean[]  restored = restoreState( true, true );
          
           delegate.serialiseToBEncodedFile( target_file );
          
           if ( target_file.equals( file )){
            
             fluff_dirty = false;
           }
          
           if ( restored[0] ){
            
             discardPieces( SystemTime.getCurrentTime(), true );
           }
          
           if ( restored[1] ){
            
             for (Iterator it = torrentFluffKeyset.iterator(); it.hasNext();){
              
               delegate.setAdditionalMapProperty( (String)it.next(), fluffThombstone );
             }
           }
      }finally{
       
        getMonitor().exit();
      }
    }


    public Map
    serialiseToMap()
       
      throws TOTorrentException
    {
        // make sure pieces are current
     
      try{
           getMonitor().enter();
          
           boolean[]  restored = restoreState( true, true );
     
           Map  result = delegate.serialiseToMap();
          
           if ( restored[0] ){
            
             discardPieces( SystemTime.getCurrentTime(), true );
           }
          
           if ( restored[1]){
       
          for (Iterator it = torrentFluffKeyset.iterator(); it.hasNext();){
         
            delegate.setAdditionalMapProperty((String) it.next(), fluffThombstone);
          }
        }
          
           return( result );
          
      }finally{
       
        getMonitor().exit();
      }
    }


    public void
    serialiseToXMLFile(
      File    target_file )
       
       throws TOTorrentException
    {
        // make sure pieces are current
     
      try{
           getMonitor().enter();
          
           boolean[]  restored = restoreState( true, true );
     
           delegate.serialiseToXMLFile( target_file );
     
           if ( restored[0] ){
            
             discardPieces( SystemTime.getCurrentTime(), true );
           }
          
           if ( restored[1]){
       
          for (Iterator it = torrentFluffKeyset.iterator(); it.hasNext();){
         
            delegate.setAdditionalMapProperty((String) it.next(), fluffThombstone);
          }
        }
      }finally{
       
        getMonitor().exit();
      }
    }

     public void
    addListener(
      TOTorrentListener    l )
    {
       delegate.addListener( l );
    }

    public void
    removeListener(
      TOTorrentListener    l )
    {
       delegate.removeListener( l );
    }
   
    public AEMonitor
    getMonitor()
    {
         return( delegate.getMonitor());
    }


    public void
    print()
    {
      delegate.print();
    }

    public String getRelationText() {
      if (delegate instanceof LogRelation)
        return ((LogRelation)delegate).getRelationText();
      return delegate.toString();
    }

    public Object[] getQueryableInterfaces() {
      if (delegate instanceof LogRelation)
        return ((LogRelation)delegate).getQueryableInterfaces();
      return super.getQueryableInterfaces();
    }

    public String getUTF8Name() {
      return delegate.getUTF8Name();
    }
  }

  /**
   * Copy a file to the Torrent Save Directory, taking into account all the
   * user config options related to that.
   * <p>
   * Also makes the directory if it doesn't exist.
   * 
   * @param f File to copy
   * @param persistent Whether the torrent is persistent
   * @return File after it's been copied (may be the same as f)
   * @throws IOException
   */
  public static File copyTorrentFileToSaveDir(File f, boolean persistent)
      throws IOException {
    File torrentDir;
    boolean saveTorrents = persistent
        && COConfigurationManager.getBooleanParameter("Save Torrent Files");
    if (saveTorrents)
      torrentDir = new File(COConfigurationManager
          .getDirectoryParameter("General_sDefaultTorrent_Directory"));
    else
      torrentDir = new File(f.getParent());

    //if the torrent is already in the completed files dir, use this
    //torrent instead of creating a new one in the default dir
    boolean moveWhenDone = COConfigurationManager.getBooleanParameter("Move Completed When Done");
    String completedDir = COConfigurationManager.getStringParameter(
        "Completed Files Directory", "");
    if (moveWhenDone && completedDir.length() > 0) {
      File cFile = new File(completedDir, f.getName());
      if (cFile.exists()) {
        //set the torrentDir to the completedDir
        torrentDir = new File(completedDir);
      }
    }

    FileUtil.mkdirs(torrentDir);

    File fDest = new File(torrentDir, f.getName().replaceAll("%20", "."));
    if (fDest.equals(f)) {
      return f;
    }

    while (fDest.exists()) {
      fDest = new File(torrentDir, "_" + fDest.getName());
    }

    fDest.createNewFile();

    if (!FileUtil.copyFile(f, fDest)) {
      throw new IOException("File copy failed");
    }

    return fDest;
  }

  /**
   * Get the DownloadManager related to a torrent's hashBytes
   *
   * @param hashBytes
   * @return
   */
  public static DownloadManager getDownloadManager( HashWrapper  hash ) {
    try {
      return AzureusCoreFactory.getSingleton().getGlobalManager()
          .getDownloadManager(hash);
    } catch (Exception e) {
      return null;
    }
  }

  /**
   * Deletes the given dir and all dirs underneath if empty.
   * Don't delete default save path or completed files directory, however,
   * allow deletion of their empty subdirectories
   * Files defined to be ignored for the sake of torrent creation are automatically deleted
   * For example, by default this includes thumbs.db
   */
  public static void recursiveEmptyDirDelete(File f) {
    TorrentUtils.recursiveEmptyDirDelete(f, true);
  }

  /**
   * Same as #recursiveEmptyDirDelete(File), except allows disabling of logging
   * of any warnings
   *
   * @param f Dir to delete
   * @param log_warnings Whether to log warning
   */
  public static void recursiveEmptyDirDelete(File f, boolean log_warnings) {
    Set ignore_map = getIgnoreSet();

    FileUtil.recursiveEmptyDirDelete(f, ignore_map, log_warnings);
  }

  /**
   * A nice string of a Torrent's hash
   *
   * @param torrent Torrent to fromat hash of
   * @return Hash string in a nice format
   */
  public static String nicePrintTorrentHash(TOTorrent torrent) {
    return nicePrintTorrentHash(torrent, false);
  }

  /**
   * A nice string of a Torrent's hash
   *
   * @param torrent Torrent to fromat hash of
   * @param tight No spaces between groups of numbers
   *
   * @return Hash string in a nice format
   */
  public static String nicePrintTorrentHash(TOTorrent torrent, boolean tight) {
    byte[] hash;

    if (torrent == null) {

      hash = new byte[20];
    } else {
      try {
        hash = torrent.getHash();

      } catch (TOTorrentException e) {

        Debug.printStackTrace(e);

        hash = new byte[20];
      }
    }

    return (ByteFormatter.nicePrint(hash, tight));
  }

  /**
   * Runs a file through a series of test to verify if it is a torrent.
   *
   * @param filename File to test
   * @return true - file is a valid torrent file
   *
   * @throws FileNotFoundException
   * @throws IOException
   */
  public static boolean isTorrentFile(String filename) throws FileNotFoundException, IOException {
    File check = new File(filename);
    if (!check.exists())
      throw new FileNotFoundException("File "+filename+" not found.");
    if (!check.canRead())
      throw new IOException("File "+filename+" cannot be read.");
    if (check.isDirectory())
      throw new FileIsADirectoryException("File "+filename+" is a directory.");
    try {
      TOTorrentFactory.deserialiseFromBEncodedFile(check);
      return true;
    } catch (Throwable e) {
      return false;
    }
  }
 
  public static void
  addCreatedTorrent(
    TOTorrent    torrent )
  {
    synchronized( created_torrents ){
     
      try{
        byte[]  hash = torrent.getHash();
       
        //System.out.println( "addCreated:" + new String(torrent.getName()) + "/" + ByteFormatter.encodeString( hash ));
       
        if ( created_torrents.size() == 0 ){
         
          COConfigurationManager.setParameter( "my.created.torrents", created_torrents );
        }
       
        HashWrapper  hw = new HashWrapper( hash );
       
        if ( !created_torrents_set.contains( hw )){
         
          created_torrents.add( hash );
       
          created_torrents_set.add( hw );
       
          COConfigurationManager.setDirty();
        }
      }catch( TOTorrentException e ){
       
      }
    }
  }
 
  public static void
  removeCreatedTorrent(
    TOTorrent    torrent )
  {
    synchronized( created_torrents ){

      try{
        HashWrapper  hw = torrent.getHashWrapper();
       
        byte[]    hash  = hw.getBytes();
       
        //System.out.println( "removeCreated:" + new String(torrent.getName()) + "/" + ByteFormatter.encodeString( hash ));

        Iterator  it = created_torrents.iterator();
       
        while( it.hasNext()){
         
          byte[]  h = (byte[])it.next();
         
          if ( Arrays.equals( hash, h )){
           
            it.remove();
          }
        }
       
        COConfigurationManager.setDirty();

        created_torrents_set.remove( hw );
       
      }catch( TOTorrentException e ){
       
      }
    }
  }
 
  public static boolean
  isCreatedTorrent(
    TOTorrent    torrent )
  {
    synchronized( created_torrents ){

      try{
        HashWrapper  hw = torrent.getHashWrapper();
       
        boolean  res = created_torrents_set.contains( hw );
       
          // if we don't have a persistent record of creation, check the non-persisted version
       
        if ( !res ){
         
          res = torrent.isCreated();
        }
       
        // System.out.println( "isCreated:" + new String(torrent.getName()) + "/" + ByteFormatter.encodeString( hw.getBytes()) + " -> " + res );

        return( res );
       
      }catch( TOTorrentException e ){
       
        Debug.printStackTrace(e);
       
        return( false );
       
      }
    }
  }
   
  private static void
  fireAttributeListener(
    TOTorrent    torrent, 
    String      attribute,
    Object      value )
  {
    Iterator it = torrent_attribute_listeners.iterator();
   
    while( it.hasNext()){
     
      try{
        ((torrentAttributeListener)it.next()).attributeSet(torrent, attribute, value);
       
      }catch( Throwable e ){
       
        Debug.printStackTrace(e);
      }
    }
  }
   
  public static void
  addTorrentAttributeListener(
    torrentAttributeListener  listener )
  {
    torrent_attribute_listeners.add( listener );
  }
 
  public static void
  removeTorrentAttributeListener(
    torrentAttributeListener  listener )
  {
    torrent_attribute_listeners.remove( listener );
  }
 
  public interface
  torrentAttributeListener
  {
    public void
    attributeSet(
      TOTorrent  torrent,
      String    attribute,
      Object    value );
  }
}
TOP

Related Classes of org.gudy.azureus2.core3.util.TorrentUtils$torrentAttributeListener

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.