Package com.aelitis.azureus.core.devices.impl

Source Code of com.aelitis.azureus.core.devices.impl.DeviceImpl

/*
* Created on Jan 28, 2009
* Created by Paul Gardner
*
* Copyright 2009 Vuze, Inc.  All rights reserved.
*
* 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; version 2 of the License only.
*
* 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.
*
* 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 com.aelitis.azureus.core.devices.impl;

import java.io.File;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.gudy.azureus2.core3.internat.MessageText;
import org.gudy.azureus2.core3.util.*;
import org.gudy.azureus2.plugins.disk.DiskManagerFileInfo;

import com.aelitis.azureus.core.devices.Device;
import com.aelitis.azureus.core.devices.DeviceListener;
import com.aelitis.azureus.core.devices.DeviceMediaRenderer;
import com.aelitis.azureus.core.devices.TranscodeException;
import com.aelitis.azureus.core.devices.TranscodeFile;
import com.aelitis.azureus.core.devices.TranscodeProfile;
import com.aelitis.azureus.core.devices.TranscodeProvider;
import com.aelitis.azureus.core.devices.TranscodeTarget;
import com.aelitis.azureus.core.devices.TranscodeTargetListener;
import com.aelitis.azureus.core.util.CopyOnWriteList;
import com.aelitis.azureus.core.vuzefile.VuzeFile;
import com.aelitis.azureus.util.ImportExportUtils;

public abstract class
DeviceImpl
  implements Device
{
  private static final String MY_PACKAGE = "com.aelitis.azureus.core.devices.impl";
 
  private static final TranscodeProfile blank_profile =
    new TranscodeProfile()
    {
      public String
      getUID()
      {
        return( null );
      }
     
      public String
      getName()
      {
        return( "blank" );
      }
     
      public String
      getDescription()
      {
        return( "blank" );
      }
     
      public boolean
      isStreamable()
      {
        return( false );
      }
     
      public String
      getIconURL()
      {
        return( null );
      }
     
      public int
      getIconIndex()
      {
        return( 0 );
      }
     
      public String
      getFileExtension()
      {
        return( null );
      }
     
      public String
      getDeviceClassification()
      {
        return( null );
      }
     
      public TranscodeProvider
      getProvider()
      {
        return( null );
      }
     
      public File
      getAssetDirectory()
      {
        return( null );
      }
    };
   
  protected static DeviceImpl
  importFromBEncodedMapStatic(
    DeviceManagerImpl  manager,
    Map          map )
 
    throws IOException
  {
    String  impl = ImportExportUtils.importString( map, "_impl" );
   
    if ( impl.startsWith( "." )){
     
      impl = MY_PACKAGE + impl;
    }
   
    try{
      Class<DeviceImpl> cla = (Class<DeviceImpl>) Class.forName( impl );
     
      Constructor<DeviceImpl> cons = cla.getDeclaredConstructor( DeviceManagerImpl.class, Map.class );
     
      cons.setAccessible( true );
     
      return( cons.newInstance( manager, map ));
     
    }catch( Throwable e ){

      Debug.out( "Can't construct device for " + impl, e );
     
      throw( new IOException( "Construction failed: " + Debug.getNestedExceptionMessage(e)));
    }
  }
 
 
  private static List<Pattern> device_renames = new ArrayList<Pattern>();
 
  static{
    try{
      device_renames.add( Pattern.compile( "TV\\s*+\\(([^\\)]*)\\)", Pattern.CASE_INSENSITIVE ));
   
     
    }catch( Throwable e ){
     
      Debug.out( e );
    }
  }
 
  private static String
  modifyDeviceDisplayName(
    String    name )
  {
    for ( Pattern p: device_renames ){
   
      Matcher m = p.matcher( name );
   
      if ( m.find()){
     
        String  new_name = m.group(1);
               
        return( new_name );
      }
    }
 
    if ( name.startsWith( "WDTVLIVE")){
     
      name = "WD TV Live";
    }
   
    return( name );
  }
 
  private static final String PP_REND_WORK_DIR    = "tt_work_dir";
  private static final String PP_REND_DEF_TRANS_PROF  = "tt_def_trans_prof";
  private static final String PP_REND_TRANS_REQ    = "tt_req";
  private static final String PP_REND_TRANS_CACHE    = "tt_always_cache";
  private static final String PP_REND_RSS_PUB      = "tt_rss_pub";
 
  protected static final String PP_REND_SHOW_CAT      = "tt_show_cat";
  protected static final String PP_REND_CLASSIFICATION  = "tt_rend_class";

  protected static final String  PP_IP_ADDRESS     = "rend_ip"
  protected static final String  TP_IP_ADDRESS     = "DeviceUPnPImpl:ip"// transient
  protected static final String  PP_FILTER_FILES   = "rend_filter";
  protected static final String  PP_RESTRICT_ACCESS  = "restrict_access"

  protected static final String  PP_COPY_OUTSTANDING = "copy_outstanding";
  protected static final String  PP_AUTO_START    = "auto_start";
 
 
  protected static final String  PP_COPY_TO_FOLDER  = "copy_to_folder";
  protected static final String  PP_AUTO_COPY    = "auto_copy";
  protected static final String  PP_EXPORTABLE    = "exportable";
 
  protected static final String  PP_LIVENESS_DETECTABLE  = "live_det";

  protected static final String  PP_TIVO_MACHINE    = "tivo_machine";

  protected static final String  PP_OD_ENABLED      = "od_enabled";
  protected static final String  PP_OD_SHOWN_FTUX    = "od_shown_ftux";
  protected static final String  PP_OD_MANUFACTURER    = "od_manufacturer";
  protected static final String  PP_OD_STATE_CACHE    = "od_state_cache";
  protected static final String  PP_OD_XFER_CACHE    = "od_xfer_cache";
  protected static final String  PP_OD_UPNP_DISC_CACHE  = "od_upnp_cache";

  protected static final boolean  PR_AUTO_START_DEFAULT  = true;
  protected static final boolean  PP_AUTO_COPY_DEFAULT  = false;


 
  private static final String  GENERIC = "generic";
 
 
  private DeviceManagerImpl  manager;
  private int          type;
  private String        uid;
  private String        secondary_uid;
  private String         classification;
  private String        name;
  private boolean        manual;
 
  private boolean      hidden;
  private boolean isGenericUSB;
  private long      last_seen;
  private boolean      can_remove = true;
 
  private int        busy_count;
  private boolean      online;
 
  private boolean      transcoding;
 
  private Map<String,Object>  persistent_properties   = new LightHashMap<String, Object>(1);

  private Map<Object,Object>  transient_properties   = new LightHashMap<Object, Object>(1);
 
  private long            device_files_last_mod;
  private boolean            device_files_dirty;
  private Map<String,Map<String,?>>  device_files;
 
  private WeakReference<Map<String,Map<String,?>>> device_files_ref;
 
  private CopyOnWriteList<TranscodeTargetListener>  listeners = new CopyOnWriteList<TranscodeTargetListener>();
 
  private Map<Object,String>  errors   = new HashMap<Object, String>();
  private Map<Object,String>  infos  = new HashMap<Object, String>();
 
  private CopyOnWriteList<DeviceListener>    device_listeners;

  private String image_id;

  private boolean isNameAutomatic;

  protected
  DeviceImpl(
    DeviceManagerImpl  _manager,
    int          _type,
    String        _uid,
    String        _classification,
    boolean        _manual )
  {
    this( _manager, _type, _uid, _classification, _manual, _classification );
  }
 
  protected
  DeviceImpl(
    DeviceManagerImpl  _manager,
    int          _type,
    String        _uid,
    String        _classification,
    boolean        _manual,
    String        _name )
  {
    manager      = _manager;
    type      = _type;
    uid        = _uid;
    classification  = _classification;
    name      = modifyDeviceDisplayName( _name );
    manual      = _manual;
    isNameAutomatic = true;
  }
 
  protected
  DeviceImpl(
    DeviceManagerImpl  _manager,
    Map          map )
 
    throws IOException
  {
    manager  = _manager;
   
    type      = (int)ImportExportUtils.importLong( map, "_type" );
    uid        = ImportExportUtils.importString( map, "_uid" );
    classification  = ImportExportUtils.importString( map, "_name" );
    name      = ImportExportUtils.importString( map, "_lname" );
    isNameAutomatic = ImportExportUtils.importBoolean( map, "_autoname", true );
    image_id      = ImportExportUtils.importString( map, "_image_id" );
   
    if ( name == null ){
     
      name = classification;
    }
   
    if ( ImportExportUtils.importLong( map, "_rn", 0 ) == 0 ){
   
      name = modifyDeviceDisplayName( name );
    }
   
    secondary_uid    = ImportExportUtils.importString( map, "_suid" );

    last_seen  = ImportExportUtils.importLong( map, "_ls" );
    hidden    = ImportExportUtils.importBoolean( map, "_hide" )
    isGenericUSB = ImportExportUtils.importBoolean( map, "_genericUSB" )
    manual    = ImportExportUtils.importBoolean( map, "_man" );

    if ( map.containsKey( "_pprops" )){
     
      persistent_properties = (Map<String,Object>)map.get( "_pprops" );
    }
  }
 
  protected void
  exportToBEncodedMap(
    Map          map )
 
    throws IOException
  {
    String  cla = this.getClass().getName();
   
    if ( cla.startsWith( MY_PACKAGE )){
     
      cla = cla.substring( MY_PACKAGE.length());
    }
   
    ImportExportUtils.exportString( map, "_impl", cla );
    ImportExportUtils.exportLong( map, "_type", new Long( type ));
    ImportExportUtils.exportString( map, "_uid", uid );
    ImportExportUtils.exportString( map, "_name", classification );
    ImportExportUtils.exportBoolean( map, "_autoname", isNameAutomatic );
    ImportExportUtils.exportLong( map, "_rn", 1 );
    ImportExportUtils.exportString( map, "_lname", name );
    ImportExportUtils.exportString( map, "_image_id", image_id );
   
    if ( secondary_uid != null ){
     
      ImportExportUtils.exportString( map, "_suid", secondary_uid );
    }
   
    ImportExportUtils.exportLong( map, "_ls", new Long( last_seen ));
    ImportExportUtils.exportBoolean( map, "_hide", hidden );
    ImportExportUtils.exportBoolean( map, "_genericUSB", isGenericUSB )
    ImportExportUtils.exportBoolean( map, "_man", manual );
   
    map.put( "_pprops", persistent_properties );
  }
 
  protected boolean
  updateFrom(
    DeviceImpl    other,
    boolean      is_alive )
  {
    if ( type != other.type ){
     
      Debug.out( "Inconsistent update operation (type)" );
     
      return( false );     
    }
   
    String  o_uid   = other.uid;
    String  o_suid  = other.secondary_uid;
   
    if ( !uid.equals( o_uid )){
     
      boolean borked = false;
     
      if ( secondary_uid == null && o_suid == null ){
     
        borked = true;
     
      }else if (   secondary_uid == null && uid.equals( o_suid ) ||
            o_suid == null && o_uid.equals( secondary_uid ) ||
            o_suid.equals( secondary_uid )){
       
      }else{
       
        borked = true;
      }
     
      if ( borked ){
       
        Debug.out( "Inconsistent update operation (uids)" );
       
        return( false );

      }
    }
   
    if ( !classification.equals( other.classification )){
     
      classification  = other.classification;
     
      setDirty();
    }
   
    /* don't overwite the name as user may have altered it!
    if ( !name.equals( other.name )){
     
      name  = other.name;
     
      setDirty();
    }
    */
   
    if ( manual != other.manual ){
     
      manual  = other.manual;
     
      setDirty();
    }
   
    if ( is_alive ){
   
      alive();
    }
   
    return( true );
  }
 
  public void
  setExportable(
    boolean    b )
  {
    setPersistentBooleanProperty( PP_EXPORTABLE, b );
  }
 
  public boolean
  isExportable()
  {
    return( getPersistentBooleanProperty( PP_EXPORTABLE, false ));
  }
 
  public VuzeFile
  getVuzeFile()
 
    throws IOException
  {
    return( manager.exportVuzeFile( this ));
  }
 
  protected void
  initialise()
  {
    updateStatus( 0 );
  }
 
  protected void
  destroy()
  { 
  }
 
  public int
  getType()
  {
    return( type );
  }
 
  public String
  getID()
  {
    return( uid );
  }
 
  protected void
  setSecondaryID(
    String    str )
  {
    secondary_uid = str;
  }
 
  protected String
  getSecondaryID()
  {
    return( secondary_uid );
  }
 
  public String getImageID() {
    return image_id;
  }
 
  public void setImageID(String id) {
    image_id = id;
  }
 
  public Device
  getDevice()
  {
    return( this );
  }

  public String
  getName()
  {
    return( name );
  }
 
  public void
  setName(
    String _name,
    boolean isAutomaticName )
  {
    if ( !name.equals( _name ) || isNameAutomatic != isAutomaticName ){
     
      name = _name;
      isNameAutomatic = isAutomaticName;
     
      setDirty();
    }
  }
 
  public boolean
  isNameAutomatic()
  {
    return isNameAutomatic;
  }
 
  public String
  getClassification()
  {
    String explicit_classification = getPersistentStringProperty( PP_REND_CLASSIFICATION, null );
   
    if ( explicit_classification != null ){
     
      return( explicit_classification );
    }
   
    return( classification );
  }
 
  public String
  getShortDescription()
  {
    if ( getRendererSpecies() == DeviceMediaRenderer.RS_ITUNES ){
     
      return( "iPad, iPhone, iPod, Apple TV" );
    }
   
    return( null );
  }
 
  public int
  getRendererSpecies()
  {
      // note, overridden in itunes
   
    if ( classification.equalsIgnoreCase( "PS3" )){
     
      return( DeviceMediaRenderer.RS_PS3 );
     
    }else if ( classification.equalsIgnoreCase( "XBox 360" )){
     
      return( DeviceMediaRenderer.RS_XBOX );
   
    }else if ( classification.equalsIgnoreCase( "Wii" )){

      return( DeviceMediaRenderer.RS_WII );
     
    }else if ( classification.equalsIgnoreCase( "Browser" )){

      return( DeviceMediaRenderer.RS_BROWSER );
     
    }else{
     
      return( DeviceMediaRenderer.RS_OTHER );
   
  }
 
  protected String
  getDeviceClassification()
  {
      // note, overridden in itunes
   
      // bit of a mess here. First release used name as classification and mapped to
      // species + device-classification here.
      // second release moved to separate name and classification and used the correct
      // device-classification as the classification
      // so we deal with both for the moment...
      // 'generic' means one we don't explicitly support, which are rendereres discovered
      // by UPnP
   
    switch( getRendererSpecies()){
   
      case DeviceMediaRenderer.RS_PS3:{
       
        return( "sony.PS3" );
      }
      case DeviceMediaRenderer.RS_XBOX:{
       
        return( "microsoft.XBox" );
      }
      case DeviceMediaRenderer.RS_WII:{
       
        return( "nintendo.Wii" );
      }
      case DeviceMediaRenderer.RS_BROWSER:{
       
        return( "browser.generic" );
      }
      case DeviceMediaRenderer.RS_OTHER:{
       
        if ( isManual()){
         
          return( classification );
        }
       
        if (   classification.equals( "sony.PSP" ) ||
            classification.startsWith( "tivo." )){
         
          return( classification )
        }
       
        String str = getPersistentStringProperty( PP_REND_CLASSIFICATION, null );
       
        if ( str != null ){
         
          return( str );
        }
 
        return( GENERIC );
      }
      default:{
        Debug.out( "Unknown classification" );
       
        return( GENERIC );
      }
    }
  }
 
  public boolean
  isNonSimple()
  {
      // apparently wmp isn't ready for the right chasm
   
    return( getDeviceClassification() == GENERIC || getClassification().startsWith( "ms_wmp." ) || isGenericUSB());
  }
   
  public boolean
  isManual()
  {
    return( manual );
  }
 
  public boolean
  isHidden()
  {
    return( hidden );
  }
 
  public void
  setHidden(
    boolean    h )
  {
    if ( h != hidden ){
     
      hidden  = h;
     
      setDirty();
    }
  }

 
  public boolean
  isGenericUSB()
  {
    return( isGenericUSB );
  }
 
  public void
  setGenericUSB(
    boolean    is )
  {
    if ( is != isGenericUSB ){
     
      isGenericUSB  = is;
     
      setDirty();
    }
  }

  public void
  alive()
  {
    last_seen  = SystemTime.getCurrentTime();
     
    if ( !online ){
   
      online  = true;
     
      setDirty( false );
    }
  }
 
  public boolean
  isLivenessDetectable()
  {
    return( !manual );
  }
 
  public boolean
  isAlive()
  {
    return( online );
  }
 
  protected void
  dead()
  {   
    if ( online ){
   
      online  = false;
     
      setDirty( false );
    }
  }
 
  protected void
  setDirty()
  {
    setDirty( true );
  }
 
  protected void
  setDirty(
    boolean    save_changes )
  {
    manager.configDirty( this, save_changes );
  }
 
  protected void
  updateStatus(
    int    tick_count )
  { 
  }
 
  public void
  requestAttention()
  {
    manager.requestAttention( this );
  }
 
  public TranscodeFileImpl[]
  getFiles()
  {
    try{
      synchronized( this ){
       
        if ( device_files == null ){
         
          loadDeviceFile();
        }
       
        List<TranscodeFile> result = new ArrayList<TranscodeFile>();
               
        Iterator<Map.Entry<String,Map<String,?>>> it = device_files.entrySet().iterator();
         
        while( it.hasNext()){
         
          Map.Entry<String,Map<String,?>> entry = it.next();
         
          try{
            TranscodeFileImpl tf = new TranscodeFileImpl( this, entry.getKey(), device_files );
           
            result.add( tf );
           
          }catch( Throwable e ){
           
            it.remove();
           
            log( "Failed to deserialise transcode file", e );
          }
        }
       
        return( result.toArray( new TranscodeFileImpl[ result.size() ]));
      }
    }catch( Throwable e ){
     
      Debug.out( e );
     
      return( new TranscodeFileImpl[0] );
    }
  }
 
  public TranscodeFileImpl
  allocateFile(
    TranscodeProfile    profile,
    boolean          no_xcode,
    DiskManagerFileInfo    file,
    boolean          for_job )
 
    throws TranscodeException
  {
    TranscodeFileImpl  result = null;
   
    try{
      synchronized( this ){
       
        if ( device_files == null ){
         
          loadDeviceFile();
        }
 
        String  key = ByteFormatter.encodeString( file.getDownloadHash() ) + ":" + file.getIndex() + ":" + profile.getUID();
               
        if ( device_files.containsKey( key )){
       
          try{         
            result = new TranscodeFileImpl( this, key, device_files );
           
          }catch( Throwable e ){
           
            device_files.remove( key );
           
            log( "Failed to deserialise transcode file", e );
          }
        }
       
        if ( result == null ){
             
          String ext = profile.getFileExtension();
         
          String  target_file = file.getFile( true ).getName();
         
          if ( ext != null && !no_xcode ){
           
            int  pos = target_file.lastIndexOf( '.' );
           
            if ( pos != -1 ){
             
              target_file = target_file.substring( 0, pos );
            }
           
            target_file += ext;
          }
           
          target_file = allocateUniqueFileName( target_file );
         
          File output_file = getWorkingDirectory( true );
 
          output_file = new File( output_file.getAbsoluteFile(), target_file );
 
          result = new TranscodeFileImpl( this, key, profile.getName(), device_files, output_file, for_job );
             
          result.setSourceFile( file );
         
          device_files_last_mod = SystemTime.getMonotonousTime();
         
          device_files_dirty  = true;
         
        }else{
         
          result.setSourceFile( file );
         
          result.setProfileName( profile.getName());
        }
      }
    }catch( Throwable e ){
     
      throw( new TranscodeException( "File allocation failed", e ));
    }
   
    for ( TranscodeTargetListener l: listeners ){
     
      try{
        l.fileAdded( result );
       
      }catch( Throwable e ){
       
        Debug.out( e );
      }
    }
   
    return( result );
  }
 
  protected String
  allocateUniqueFileName(
    String    str )
  {
    Set<String> name_set = new HashSet<String>();
   
    for (Map<String,?> entry: device_files.values()){
     
      try{
        name_set.add( new File( ImportExportUtils.importString( entry, TranscodeFileImpl.KEY_FILE )).getName());
       
      }catch( Throwable e ){
       
        Debug.out( e );
      }
    }

    for (int i=0;i<1024;i++){
     
      String  test_name = i==0?str:( i + "_" + str);
     
      if ( !name_set.contains( test_name )){
     
        str = test_name;
       
        break;
      }       
    }
   
    return( str );
  }
 
  protected void
  revertFileName(
    TranscodeFileImpl  tf )
 
    throws TranscodeException
  {
    File cache_file = tf.getCacheFile();
   
    if ( cache_file.exists()){
     
      Debug.out( "Cache file already allocated, can't rename" );
     
      return;
    }
   
    File source_file = tf.getSourceFile().getFile( true );
   
    String  original_name = source_file.getName();
   
    int pos = original_name.indexOf('.');
   
    if ( pos == -1 ){
     
      return;
    }
   
    String  cf_name = cache_file.getName();
   
    if ( cf_name.endsWith( original_name.substring(pos))){
     
      return;
    }
   
    try{
      synchronized( this ){
       
        if ( device_files == null ){
         
          loadDeviceFile();
        }
       
        String reverted_name = allocateUniqueFileName( original_name );
       
        tf.setCacheFile( new File( cache_file.getParentFile(), reverted_name ));
      }
    }catch( Throwable e ){
     
      throw( new TranscodeException( "File name revertion failed", e ));
    }
  }
 
  public TranscodeFileImpl
  lookupFile(
    TranscodeProfile    profile,
    DiskManagerFileInfo    file )
  {
    try{
      synchronized( this ){
       
        if ( device_files == null ){
         
          loadDeviceFile();
        }
 
        String  key = ByteFormatter.encodeString( file.getDownloadHash() ) + ":" + file.getIndex() + ":" + profile.getUID();
               
        if ( device_files.containsKey( key )){
       
          try{         
            return( new TranscodeFileImpl( this, key, device_files ));
           
          }catch( Throwable e ){
           
            device_files.remove( key );
           
            log( "Failed to deserialise transcode file", e );
          }
        }
      }
    }catch( Throwable e ){
    }
   
    return( null );
  }
 
  protected TranscodeFileImpl
  getTranscodeFile(
    String    key )
  {
    try{
      synchronized( this ){
       
        if ( device_files == null ){
         
          loadDeviceFile();
        }
                 
        if ( device_files.containsKey( key )){
       
          try{         

            return( new TranscodeFileImpl( this, key, device_files ));
           
        catch( Throwable e ){
           
            device_files.remove( key );
           
            log( "Failed to deserialise transcode file", e );
          }
        }
      }
    }catch( Throwable e ){
    }
   
    return( null );
  }
 
  public File
  getWorkingDirectory()
  {
    return( getWorkingDirectory( false ));
  }
 
  public File
  getWorkingDirectory(
    boolean  persist )
  {       
    String result = getPersistentStringProperty( PP_REND_WORK_DIR );
   
    if ( result.length() == 0 ){
     
      File f = manager.getDefaultWorkingDirectory( persist );
     
      if ( persist ){
     
        f.mkdirs();
      }
     
      String  name = FileUtil.convertOSSpecificChars( getName(), true );
     
      for (int i=0;i<1024;i++){
       
        String test_name = name + (i==0?"":("_"+i));
       
        File test_file = new File( f, test_name );
       
        if ( !test_file.exists()){
     
          f = test_file;
         
          break;
        }
      }
     
      result = f.getAbsolutePath();
     
      if ( persist ){
     
        setPersistentStringProperty( PP_REND_WORK_DIR, result );
      }
    }
   
    File f_result = new File( result );
   
    if ( !f_result.exists()){
     
      if ( persist ){
     
        f_result.mkdirs();
      }
    }
   
    return( f_result );
  }
 
  public void
  setWorkingDirectory(
    File    directory )
  {
    setPersistentStringProperty( PP_REND_WORK_DIR, directory.getAbsolutePath());
  }

  protected void
  resetWorkingDirectory()
  {
    setPersistentStringProperty( PP_REND_WORK_DIR, "" );
  }
 
  public TranscodeProfile[]
  getTranscodeProfiles()
  {
    return getTranscodeProfiles(true);
  }

 
  public TranscodeProfile[]
  getTranscodeProfiles(boolean walkup)
  {   
    String classification = getDeviceClassification();
   
    TranscodeProfile[] result = getTranscodeProfiles( classification );
   
    if ( !walkup || result.length > 0 ){
     
      return( result );
    }
   
    try{
      String[]  bits = Constants.PAT_SPLIT_DOT.split(classification);
     
        // I would like to drill all the way up to just 'generic' but unfortunately this
        // would break the current samsung/ms_wmp support that requires the detected profile
        // set to be empty (we have two existing profiles at the 'generic' level)
     
      for ( int i=bits.length-1;i>=1;i--){
       
        String c = "";
       
        for (int j=0;j<i;j++){
         
          c = c + (c.length()==0?"":".") + bits[j];
        }
       
        c = c + (c.length()==0?"":".") + "generic";
       
        result = getTranscodeProfiles( c );
       
        if ( result.length > 0 ){
         
          return( result );
        }
      }
    }catch( Throwable e ){
 
      Debug.out( e );
    }
   
    return( new TranscodeProfile[0] );
  }
   
  private TranscodeProfile[]
  getTranscodeProfiles(
    String    classification )
  {   
    List<TranscodeProfile>  profiles = new ArrayList<TranscodeProfile>();
   
    DeviceManagerImpl dm = getManager();
           
    TranscodeProvider[] providers = dm.getProviders();
           
    for ( TranscodeProvider provider: providers ){
     
      TranscodeProfile[] ps = provider.getProfiles();
     
      for ( TranscodeProfile p : ps ){
       
        String c = p.getDeviceClassification();
       
        if ( c == null ){
         
          log( "Device classification missing for " + p.getName());
         
        }else{
          if ( c.toLowerCase().startsWith( classification.toLowerCase())){
           
            profiles.add( p );
          }
        }
      }
    }
   
    return( profiles.toArray( new TranscodeProfile[profiles.size()] ));
  }
 
  public TranscodeProfile
  getDefaultTranscodeProfile()
  {
    String uid = getPersistentStringProperty( PP_REND_DEF_TRANS_PROF );
   
    DeviceManagerImpl dm = getManager();
   
    TranscodeManagerImpl tm = dm.getTranscodeManager();

    TranscodeProfile profile = tm.getProfileFromUID( uid );
   
    if ( profile != null ){
     
      return( profile );
    }
 
    return( null );
  }
 
  public void
  setDefaultTranscodeProfile(
    TranscodeProfile    profile )
  {
    if ( profile == null ){
   
      removePersistentProperty( PP_REND_DEF_TRANS_PROF );
     
    }else{
     
      setPersistentStringProperty( PP_REND_DEF_TRANS_PROF, profile.getUID());
    }
  }
 
  public TranscodeProfile
  getBlankProfile()
  {
    return( blank_profile );
  }
 
  protected void
  setTranscoding(
    boolean    _transcoding )
  {
    transcoding = _transcoding;
   
    manager.deviceChanged( this, false );
  }
 
  public boolean
  isTranscoding()
  {
    return( transcoding );
  }
 
  public int
  getTranscodeRequirement()
  {
    return( getPersistentIntProperty( PP_REND_TRANS_REQ, TranscodeTarget.TRANSCODE_WHEN_REQUIRED ));
  }
 
  public void
  setTranscodeRequirement(
    int    req )
  {
    setPersistentIntProperty( PP_REND_TRANS_REQ, req );
  }
 
  public boolean
  isAudioCompatible(
    TranscodeFile    file )
  {
    return( false );
  }
 
  public boolean
  getAlwaysCacheFiles()
  {
    return( getPersistentBooleanProperty( PP_REND_TRANS_CACHE, false ));
  }
 
  public void
  setAlwaysCacheFiles(
    boolean    always_cache )
  {
    setPersistentBooleanProperty( PP_REND_TRANS_CACHE, always_cache );
  }
 
  public boolean
  isRSSPublishEnabled()
  {
    return( getPersistentBooleanProperty( PP_REND_RSS_PUB, true ));
  }
 
  public void
  setRSSPublishEnabled(
    boolean    enabled )
  {
    setPersistentBooleanProperty( PP_REND_RSS_PUB, enabled );
  }
 
  public String[][]
  getDisplayProperties()
  {
    List<String[]> dp = new ArrayList<String[]>();
   
      getDisplayProperties( dp );
           
      String[][] res = new String[2][dp.size()];
    
      int  pos = 0;
     
      for ( String[] entry: dp ){
     
        res[0][pos] = entry[0];
        res[1][pos] = entry[1];
       
        pos++;
      }
     
      return( res );
  }
 
  protected void
  getDisplayProperties(
    List<String[]>  dp )
  {
    if ( !name.equals( classification )){
     
      addDP( dp, "TableColumn.header.name", name );
    }
   
    addDP( dp, "TableColumn.header.class", getClassification().toLowerCase());
   
    addDP( dp, "!UID!", getID());
   
    if ( !manual ){
   
      addDP( dp, "azbuddy.ui.table.online",  online );
   
      addDP( dp, "device.lastseen", last_seen==0?"":new SimpleDateFormat().format(new Date( last_seen )));
    }
  }
 
  protected void
  getTTDisplayProperties(
    List<String[]>  dp )
  {
    addDP( dp, "devices.xcode.working_dir", getWorkingDirectory( false ).getAbsolutePath());
 
    addDP( dp, "devices.xcode.prof_def", getDefaultTranscodeProfile());
   
    addDP( dp, "devices.xcode.profs", getTranscodeProfiles());
   
    int  tran_req = getTranscodeRequirement();
   
    String  tran_req_str;
   
    if ( tran_req == TranscodeTarget.TRANSCODE_ALWAYS ){
     
       tran_req_str = "device.xcode.always";
      
    }else if ( tran_req == TranscodeTarget.TRANSCODE_NEVER ){
     
       tran_req_str = "device.xcode.never";
    }else{
     
       tran_req_str = "device.xcode.whenreq";
    }
   
    addDP( dp, "device.xcode", MessageText.getString( tran_req_str ));
  }
 
  protected void
  addDP(
    List<String[]>  dp,
    String      name,
    String      value )
  {
    dp.add( new String[]{ name, value });
  }
 
  protected void
  addDP(
    List<String[]>  dp,
    String      name,
    File      value )
  {
    dp.add( new String[]{ name, value==null?"":value.getAbsolutePath() });
  }
  protected void
  addDP(
    List<String[]>  dp,
    String      name,
    String[]    values )
  {
    String value = "";
   
    for ( String v: values ){
     
      value += (value.length()==0?"":",") + v;
    }
   
    dp.add( new String[]{ name, value });
  }
 
  protected void
  addDP(
    List<String[]>  dp,
    String      name,
    boolean      value )
  {
    dp.add( new String[]{ name, MessageText.getString( value?"GeneralView.yes":"GeneralView.no" ) });
  }
 
 
  protected void
  addDP(
    List<String[]>    dp,
    String        name,
    TranscodeProfile  value )
  {
    addDP( dp, name, value==null?"":value.getName());
  }
 
  protected void
  addDP(
    List<String[]>    dp,
    String        name,
    TranscodeProfile[]  values )
  {
    String[]  names = new String[values.length];
   
    for (int i=0;i<values.length;i++){
     
      names[i] = values[i].getName();
    }
   
    addDP( dp, name, names);
  }
 
  public void
  setCanRemove(
    boolean  can )
  {
    can_remove = can;
  }
 
  public boolean
  canRemove()
  {
    return( can_remove );
  }
 
  public boolean
  isBusy()
  {
    if ( isTranscoding()){
     
      return( true );
    }
   
    synchronized( this ){
     
      return( busy_count > 0 );
    }
  }
 
  protected void
  setBusy(
    boolean  busy )
  {
    boolean  changed = false;
   
    synchronized( this ){ 
     
      if ( busy ){
   
        changed = busy_count++ == 0;
       
      }else{
       
        changed = busy_count-- == 1;
      }
    }
   
    if ( changed ){
     
      manager.deviceChanged( this, false );
    }
  }
 
  public void
  remove()
  {
    manager.removeDevice( this );
  }
 
  public String
  getPersistentStringProperty(
    String    prop )
  {
    return( getPersistentStringProperty( prop, "" ));
  }
 
  public String
  getPersistentStringProperty(
    String    prop,
    String    def )
  {
    synchronized( persistent_properties ){
     
      try{
        byte[]  value = (byte[])persistent_properties.get( prop );
   
        if ( value == null ){
         
          return( def );
        }
       
        return( new String( value, "UTF-8" ));
       
      }catch( Throwable e ){
       
        Debug.printStackTrace(e);
       
        return( def );
      }
    }
  }
 
  public void
  setPersistentStringProperty(
    String    prop,
    String    value )
  {
    boolean  dirty = false;
   
    synchronized( persistent_properties ){
     
      String existing = getPersistentStringProperty( prop );
     
      if ( !existing.equals( value )){
       
        try{
          if ( value == null ){
           
            persistent_properties.remove( prop );
           
          }else{
         
            persistent_properties.put( prop, value.getBytes( "UTF-8" ));
          }
         
          dirty = true;
         
        }catch( Throwable e ){
         
          Debug.printStackTrace(e);
        }
      }
    }
   
    if ( dirty ){
     
      setDirty();
    }
  }
 
  public <T> Map<String,T>
  getPersistentMapProperty(
    String          prop,
    Map<String,T>      def )
  {
    synchronized( persistent_properties ){
     
      try{
        Map<String,T>  value = (Map<String,T>)persistent_properties.get( prop );
   
        if ( value == null ){
         
          return( def );
        }
       
        return( value );
       
      }catch( Throwable e ){
       
        Debug.printStackTrace(e);
       
        return( def );
      }
    }
  }
 
  public <T>void
  setPersistentMapProperty(
    String          prop,
    Map<String,T>      value )
  {
    boolean  dirty = false;
   
    synchronized( persistent_properties ){
     
      Map<String,T> existing = getPersistentMapProperty( prop, null );
     
      if ( !BEncoder.mapsAreIdentical( value, existing )){
       
        try{
          if ( value == null ){
           
            persistent_properties.remove( prop );
           
          }else{
         
            persistent_properties.put( prop, value );
          }
         
          dirty = true;
         
        }catch( Throwable e ){
         
          Debug.printStackTrace(e);
        }
      }
    }
   
    if ( dirty ){
     
      setDirty();
    }
  }
 
  public void
  removePersistentProperty(
    String    prop )
  {
    boolean  dirty = false;
   
    synchronized( persistent_properties ){
     
      String existing = getPersistentStringProperty( prop );
     
      if ( existing != null ){
       
        try{
          persistent_properties.remove( prop );
         
          dirty = true;
         
        }catch( Throwable e ){
         
          Debug.printStackTrace(e);
        }
      }
    }
   
    if ( dirty ){
     
      setDirty();
    }
  }
  public String
  getError()
  {
    synchronized( errors ){

      if ( errors.size() == 0 ){
       
        return( null );
      }
     
      String   res = "";
     
      for ( String s: errors.values()){
       
        res += (res.length()==0?"":"; ") + s;
      }
     
      return( res );
    }
  }
 
  protected void
  setError(
    Object  key,
    String  error )
  {
    boolean  changed = false;
   
    if ( error == null || error.length() == 0 ){
     
      synchronized( errors ){
     
        changed = errors.remove( key ) != null;
      }
    }else{
     
      String  existing;
     
      synchronized( errors ){
       
        existing = errors.put( key, error );
      }
     
      changed = existing == null || !existing.equals( error );
    }
   
    if ( changed ){
     
      manager.deviceChanged( this, false );
    }
  }
 
  public String
  getInfo()
  {
    synchronized( infos ){

      if ( infos.size() == 0 ){
       
        return( null );
      }
     
      String   res = "";
     
      for ( String s: infos.values()){
       
        res += (res.length()==0?"":"; ") + s;
      }
     
      return( res );
    }
  }
 
  protected void
  setInfo(
    Object  key,
    String  info )
  {
    boolean  changed = false;
   
    if ( info == null || info.length() == 0 ){
     
      synchronized( infos ){
     
        changed = infos.remove( key ) != null;
      }
    }else{
     
      String  existing;
     
      synchronized( infos ){
       
        existing = infos.put( key, info );
      }
     
      changed = existing == null || !existing.equals( info );
    }
   
    if ( changed ){
     
      manager.deviceChanged( this, false );
    }
  }
 
  public String
  getStatus()
  {
    if ( isLivenessDetectable()){
     
      if ( isAlive()){
       
        return( MessageText.getString( "device.status.online" ));
       
      }else{
       
        return( MessageText.getString( "device.od.error.notfound" ));
      }
    }
   
    return( null );
  }
 
  public boolean
  getPersistentBooleanProperty(
    String    prop,
    boolean    def )
  {
    return( getPersistentStringProperty( prop, def?"true":"false" ).equals( "true" ));
  }
 
  public void
  setPersistentBooleanProperty(
    String    prop,
    boolean    value )
  {
    setPersistentStringProperty(prop, value?"true":"false" );
  }
 
  public int
  getPersistentIntProperty(
    String    prop,
    int      def )
  {
    return( Integer.parseInt( getPersistentStringProperty( prop, String.valueOf(def) )));
  }
 
  public void
  setPersistentIntProperty(
    String    prop,
    int      value )
  {
    setPersistentStringProperty(prop, String.valueOf( value ));
  }
 
  public String[]
  getPersistentStringListProperty(
    String    prop )
  {
    synchronized( persistent_properties ){
     
      try{
        List<byte[]>  values = (List<byte[]>)persistent_properties.get( prop );
   
        if ( values == null ){
         
          return( new String[0] );
        }
       
        String[]  res = new String[values.size()];
       
        int  pos = 0;
       
        for (byte[] value: values ){
       
          res[pos++] = new String( value, "UTF-8" );
        }
       
        return( res );
       
      }catch( Throwable e ){
       
        Debug.printStackTrace(e);
       
        return( new String[0] );
      }
    }
  }
 
  public void
  setPersistentStringListProperty(
    String      prop,
    String[]    values )
  {
    boolean dirty = false;

    synchronized( persistent_properties ){
     
      try{
        List<byte[]> values_list = new ArrayList<byte[]>();
       
        for (String value: values ){
         
          values_list.add( value.getBytes( "UTF-8" ));
        }
       
        persistent_properties.put( prop, values_list );
       
        dirty = true;
       
      }catch( Throwable e ){
       
        Debug.printStackTrace(e);
      }
    }
   
    if ( dirty ){
     
      setDirty();
    }
  }
 
  public void
  setTransientProperty(
    Object    key,
    Object    value )
  {
    synchronized( transient_properties ){
     
      if ( value == null ){
       
        transient_properties.remove( key );
       
      }else{
     
        transient_properties.put( key, value );
      }
    }
  }
 
  public Object
  getTransientProperty(
    Object    key )
  {
    synchronized( transient_properties ){
     
      return( transient_properties.get( key ));
    }
  }
 
  public void
  setTransientProperty(
    Object    key1,
    Object    key2,
    Object    value )
  {
    synchronized( transient_properties ){
     
      Map<Object,Object> l1 = (Map<Object,Object>)transient_properties.get( key1 );
     
      if ( l1 == null ){
       
        if ( value == null ){
         
          return;
        }
       
        l1 = new HashMap<Object, Object>();
       
        transient_properties.put( key1, l1 );
      }
     
      if ( value == null ){
       
        l1.remove( key2 );
       
        if ( l1.size() == 0 ){
         
          transient_properties.remove( key1 )
        }
      }else{
       
        l1.put( key2, value );
      }
    }
  }
 
  public Object
  getTransientProperty(
    Object    key1,
    Object    key2 )
  {
    synchronized( transient_properties ){
     
      Map<Object,Object> l1 = (Map<Object,Object>)transient_properties.get( key1 );
     
      if ( l1 == null ){
       
        return( null );
      }

      return( l1.get( key2 ));
    }
  }
 
  protected void
  close()
  {
    synchronized( this ){

      if ( device_files_dirty ){
       
        saveDeviceFile();
      }
    }
  }
 
  protected void
  loadDeviceFile()
 
    throws IOException
  {
    device_files_last_mod = SystemTime.getMonotonousTime();
   
    if ( device_files_ref != null ){
   
      device_files = device_files_ref.get();
    }
   
    if ( device_files == null ){
     
      Map  map = FileUtil.readResilientFile( getDeviceFile());
 
      device_files = (Map<String,Map<String,?>>)map.get( "files" );
     
      if ( device_files == null ){
       
        device_files = new HashMap<String, Map<String,?>>();
      }
   
      device_files_ref = new WeakReference<Map<String,Map<String,?>>>( device_files );
     
      log( "Loaded device file for " + getName() + ": files=" + device_files.size());
    }
   
    final int GC_TIME = 15000;
   
    new DelayedEvent(
      "Device:gc",
      GC_TIME,
      new AERunnable()
      {
        public void
        runSupport()
        {
          synchronized( DeviceImpl.this ){
           
            if ( SystemTime.getMonotonousTime() - device_files_last_mod >= GC_TIME ){
               
              if ( device_files_dirty ){
               
                saveDeviceFile();
              }
             
              device_files = null;
             
            }else{
             
              new DelayedEvent( "Device:gc2", GC_TIME, this );
            }
          }
        }
      });
  }
 
  protected URL
  getStreamURL(
    TranscodeFileImpl    file,
    String          host )
  {
    return( manager.getStreamURL( file, host ));
  }
 
  protected String
  getMimeType(
    TranscodeFileImpl    file )
  {
    return( manager.getMimeType( file ));
  }
 
  protected void
  deleteFile(
    TranscodeFileImpl  file,
    boolean        delete_contents,
    boolean        remove )
 
    throws TranscodeException
 
    if ( file.isDeleted()){
     
      return;
    }
   
    if ( delete_contents ){
     
      File f = file.getCacheFile();
       
      int   time = 0;
     
      while( f.exists() && !f.delete()){
           
        if ( time > 3000 ){
       
          log( "Failed to remove file '" + f.getAbsolutePath() + "'" );
         
          break;
         
        }else{
         
          try{
            Thread.sleep(500);
           
          }catch( Throwable e ){
           
          }
         
          time += 500;
        }
      }
    }
   
    if ( remove ){
     
      try{
          // fire the listeners FIRST as this gives listeners a chance to extract data
          // from the file before it is deleted (otherwise operations fail with 'file has been
          // deleted'
       
        for ( TranscodeTargetListener l: listeners ){
         
          try{
            l.fileRemoved( file );
           
          }catch( Throwable e ){
           
            Debug.out( e );
          }
        }
       
        synchronized( this ){
         
          if ( device_files == null ){
           
            loadDeviceFile();
           
          }else{
           
            device_files_last_mod = SystemTime.getMonotonousTime();
          }
         
          device_files.remove( file.getKey());
         
          device_files_dirty  = true;
        }
       

      }catch( Throwable e ){
       
        throw( new TranscodeException( "Delete failed", e ));
      }
    }
  }
 
  protected void
  fileDirty(
    TranscodeFileImpl  file,
    int          type,
    Object        data )
  {
    try{
      synchronized( this ){
       
        if ( device_files == null ){
         
          loadDeviceFile();
         
        }else{
         
          device_files_last_mod = SystemTime.getMonotonousTime();
        }
      }
     
      device_files_dirty  = true;
     
    }catch( Throwable e ){
     
      Debug.out( "Failed to load device file", e );
    }
   
    for ( TranscodeTargetListener l: listeners ){
     
      try{
        l.fileChanged( file, type, data );
       
      }catch( Throwable e ){
       
        Debug.out( e );
      }
    }
  }
 
  protected void
  saveDeviceFile()
  {     
    device_files_dirty = false;
   
    try{
      loadDeviceFile();
     
      if ( device_files == null || device_files.size()==0 ){
       
        FileUtil.deleteResilientFile( getDeviceFile());
       
      }else{
        Map map = new HashMap();
       
        map.put( "files", device_files );
       
        FileUtil.writeResilientFile( getDeviceFile(), map );
      }
    }catch( Throwable e ){
     
      Debug.out( "Failed to save device file", e );
    }
  }
 
  protected File
  getDeviceFile()
 
    throws IOException
  {
     File dir = getDevicesDir();
    
     return( new File( dir, FileUtil.convertOSSpecificChars(getID(),false) + ".dat" ));
  }
 
  protected File
  getDevicesDir()
 
    throws IOException
  {
    File dir = new File(SystemProperties.getUserPath());

    dir = new File( dir, "devices" );
    
     if ( !dir.exists()){
      
       if ( !dir.mkdirs()){
        
         throw( new IOException( "Failed to create '" + dir + "'" ));
       }
     } 
    
     return( dir );
  }
 
  protected DeviceManagerImpl
  getManager()
  {
    return( manager );
  }

  public void
  addListener(
    TranscodeTargetListener    listener )
  {
    if (!listeners.contains(listener)) {
      listeners.add( listener );
    }
  }
 
  public void
  removeListener(
    TranscodeTargetListener    listener )
  {
    listeners.remove( listener );
  }
 
  protected void
  fireChanged()
  {
    List<DeviceListener> l;
   
    synchronized( this ){
     
      if ( device_listeners != null ){
       
        l = device_listeners.getList();
       
      }else{
       
        return;
      }
    }
   
    for ( DeviceListener listener: l ){
     
      try{
        listener.deviceChanged( this );
       
      }catch( Throwable e ){
       
        Debug.out( e );
      }
    }
  }
 
  public void
  addListener(
    DeviceListener    listener )
  {
    synchronized( this ){
     
      if ( device_listeners == null ){
       
        device_listeners = new CopyOnWriteList<DeviceListener>();
      }
     
      device_listeners.add( listener );
    }
  }
 
  public void
  removeListener(
    DeviceListener    listener )
  {
    synchronized( this ){
     
      if ( device_listeners != null ){
             
        device_listeners.remove( listener );
       
        if ( device_listeners.size() == 0 ){
         
          device_listeners = null;
        }   
      }
    }
  }
 
  protected void
  log(
    String    str )
  {
    manager.log( str );
  }
 
  protected void
  log(
    String    str,
    Throwable  e )
  {
    manager.log( str, e );
  }
 
  public String
  getString()
  {
    return( "type=" + type + ",uid=" + uid + ",class=" + classification );
  }
 
  public void
  generate(
    IndentWriter    writer )
  {
    writer.println( getName() + "/" + getID() + "/" + type );
   
    try{
      writer.indent();
     
      writer.println(
        "hidden=" + hidden +
        ", last_seen=" + new SimpleDateFormat().format(new Date(last_seen)) +
        ", online=" + online +
        ", transcoding=" + transcoding );
     
      writer.println( "p_props=" + persistent_properties );
      writer.println( "t_props=" + transient_properties );
     
      writer.println( "errors=" + errors );
      writer.println( "infos=" + infos );
    }finally{
     
      writer.exdent();
    }
  }
 
  public void
  generateTT(
    IndentWriter    writer )
  {
    TranscodeFileImpl[] files = getFiles();
   
    int  complete   = 0;
    int  copied     = 0;
    int  deleted     = 0;
    int  template   = 0;
   
    for ( TranscodeFileImpl f: files ){
     
      if ( f.isComplete()){
       
        complete++;
      }
     
      if ( f.isCopiedToDevice()){
       
        copied++;
      }
     
      if ( f.isDeleted()){
       
        deleted++;
      }
     
      if ( f.isTemplate()){
       
        template++;
      }
    }
   
    writer.println( "files=" + files.length + ", comp=" + complete + ", copied=" + copied + ", deleted=" + deleted + ", template=" + template );
  }
 
  protected class
  browseLocationImpl
    implements browseLocation
  {
    private String    name;
    private URL      url;
   
    protected
    browseLocationImpl(
      String    _name,
      URL      _url )
    {
      name    = _name;
      url      = _url;
    }
   
    public String
    getName()
    {
      return( name );
    }
   
    public URL
    getURL()
    {
      return( url );
    }
  }
}
TOP

Related Classes of com.aelitis.azureus.core.devices.impl.DeviceImpl

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.