/*
* Created on May 6, 2008
* Created by Paul Gardner
*
* Copyright 2008 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.metasearch.impl;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URLDecoder;
import java.util.*;
import org.gudy.azureus2.core3.config.COConfigurationManager;
import org.gudy.azureus2.core3.config.ParameterListener;
import org.gudy.azureus2.core3.internat.MessageText;
import org.gudy.azureus2.core3.util.AEDiagnostics;
import org.gudy.azureus2.core3.util.BEncoder;
import org.gudy.azureus2.core3.util.Base32;
import org.gudy.azureus2.core3.util.Debug;
import org.gudy.azureus2.core3.util.RandomUtils;
import org.gudy.azureus2.core3.util.UrlUtils;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import com.aelitis.azureus.core.messenger.config.PlatformMetaSearchMessenger;
import com.aelitis.azureus.core.metasearch.Engine;
import com.aelitis.azureus.core.metasearch.Result;
import com.aelitis.azureus.core.metasearch.ResultListener;
import com.aelitis.azureus.core.metasearch.SearchException;
import com.aelitis.azureus.core.metasearch.SearchLoginException;
import com.aelitis.azureus.core.metasearch.SearchParameter;
import com.aelitis.azureus.core.metasearch.impl.plugin.PluginEngine;
import com.aelitis.azureus.core.metasearch.impl.web.json.JSONEngine;
import com.aelitis.azureus.core.metasearch.impl.web.regex.RegexEngine;
import com.aelitis.azureus.core.metasearch.impl.web.rss.RSSEngine;
import com.aelitis.azureus.core.vuzefile.VuzeFile;
import com.aelitis.azureus.core.vuzefile.VuzeFileComponent;
import com.aelitis.azureus.core.vuzefile.VuzeFileHandler;
import com.aelitis.azureus.util.ConstantsVuze;
import com.aelitis.azureus.util.ImportExportUtils;
import com.aelitis.azureus.util.JSONUtils;
public abstract class
EngineImpl
implements Engine
{
private static final int DEFAULT_UPDATE_CHECK_SECS = 24*60*60;
private static boolean logging_enabled;
static{
COConfigurationManager.addAndFireParameterListeners(
new String[]{
"Logger.Enabled",
},
new ParameterListener()
{
public void
parameterChanged(
String parameterName)
{
logging_enabled = COConfigurationManager.getBooleanParameter( "Logger.Enabled" );
}
});
}
protected static EngineImpl
importFromBEncodedMap(
MetaSearchImpl meta_search,
Map map )
throws IOException
{
int type = ((Long)map.get( "type" )).intValue();
if ( type == Engine.ENGINE_TYPE_JSON ){
return( JSONEngine.importFromBEncodedMap( meta_search, map ));
}else if ( type == Engine.ENGINE_TYPE_REGEX ){
return( RegexEngine.importFromBEncodedMap( meta_search, map ));
}else if ( type == Engine.ENGINE_TYPE_PLUGIN ){
return( PluginEngine.importFromBEncodedMap( meta_search, map ));
}else if ( type == Engine.ENGINE_TYPE_RSS ){
return( RSSEngine.importFromBEncodedMap( meta_search, map ));
}else{
throw( new IOException( "Unknown engine type " + type ));
}
}
public static Engine
importFromJSONString(
MetaSearchImpl meta_search,
int type,
long id,
long last_updated,
float rank_bias,
String name,
String content )
throws IOException
{
JSONObject map = (JSONObject)JSONUtils.decodeJSON( content );
if ( type == Engine.ENGINE_TYPE_JSON ){
return( JSONEngine.importFromJSONString( meta_search, id, last_updated, rank_bias, name, map ));
}else if ( type == Engine.ENGINE_TYPE_REGEX ){
return( RegexEngine.importFromJSONString( meta_search, id, last_updated, rank_bias, name, map ));
}else if ( type == Engine.ENGINE_TYPE_RSS ){
return( RSSEngine.importFromJSONString( meta_search, id, last_updated, rank_bias, name, map ));
}else{
throw( new IOException( "Unknown engine type " + type ));
}
}
// if you extend this with state pertaining to what has been downloaded then
// make sure you extend the code in reset();
protected static final String LD_COOKIES = "cookies";
protected static final String LD_ETAG = "etag";
protected static final String LD_LAST_MODIFIED = "last_mod";
protected static final String LD_LAST_UPDATE_CHECK = "last_update_check";
protected static final String LD_UPDATE_CHECK_SECS = "update_check_secs";
protected static final String LD_CREATED_BY_ME = "mine";
protected static final String LD_AUTO_DL_SUPPORTED = "auto_dl_supported";
protected static final String LD_LINK_IS_TORRENT = "link_is_torrent";
private MetaSearchImpl meta_search;
private int type;
private long id;
private long last_updated;
private String name;
private byte[] uid;
private int version;
private boolean is_public = true;
private int az_version;
private int selection_state = SEL_STATE_DESELECTED;
private boolean selection_state_recorded = true;
private int source = ENGINE_SOURCE_UNKNOWN;
private float rank_bias = 1;
private float preferred_count = 0;
// first mappings used to canonicalise names and map field to same field
// typically used for categories (musak->music)
private List first_level_mapping = new ArrayList();
// second mappings used to generate derived field values
// typically used to derive content_type from category (music->AUDIO)
private List second_level_mapping = new ArrayList();
// applicable to non-vuze hosted templates
private String update_url;
private int update_check_default_secs;
private Map user_data;
// manual constructor
protected
EngineImpl(
MetaSearchImpl _meta_search,
int _type,
long _id,
long _last_updated,
float _rank_bias,
String _name )
{
meta_search = _meta_search;
type = _type;
id = _id;
last_updated = _last_updated;
rank_bias = _rank_bias;
name = _name;
version = 1;
az_version = AZ_VERSION;
allocateUID( id );
}
// bencoded constructor
protected
EngineImpl(
MetaSearchImpl _meta_search,
Map map )
throws IOException
{
meta_search = _meta_search;
type = ((Long)map.get( "type" )).intValue();
Long l_id = (Long)map.get( "id");
id = l_id==null?meta_search.getManager().getLocalTemplateID():l_id.longValue();
last_updated = ImportExportUtils.importLong( map, "last_updated" );
name = ImportExportUtils.importString( map, "name" );
selection_state = (int)ImportExportUtils.importLong( map, "selected", SEL_STATE_DESELECTED );
selection_state_recorded = ImportExportUtils.importBoolean(map,"select_rec", true );
source = (int)ImportExportUtils.importLong( map, "source", ENGINE_SOURCE_UNKNOWN );
rank_bias = ImportExportUtils.importFloat( map, "rank_bias", 1.0f );
preferred_count = ImportExportUtils.importFloat( map, "pref_count", 0.0f );
first_level_mapping = importBEncodedMappings( map, "l1_map" );
second_level_mapping = importBEncodedMappings( map, "l2_map" );
version = (int)ImportExportUtils.importLong( map, "version", 1 );
az_version = (int)ImportExportUtils.importLong( map, "az_version", AZ_VERSION );
if ( az_version > AZ_VERSION ){
throw( new IOException( MessageText.getString("metasearch.template.version.bad", new String[]{ name })));
}
uid = (byte[])map.get( "uid" );
if ( uid == null ){
allocateUID( id );
}
update_url = ImportExportUtils.importString( map, "update_url" );
update_check_default_secs = (int)ImportExportUtils.importLong( map, "update_url_check_secs", DEFAULT_UPDATE_CHECK_SECS );
}
// bencoded export
protected void
exportToBencodedMap(
Map map,
boolean generic )
throws IOException
{
map.put( "type", new Long( type ));
ImportExportUtils.exportString( map, "name", name );
map.put( "source", new Long( source ));
exportBEncodedMappings( map, "l1_map", first_level_mapping );
exportBEncodedMappings( map, "l2_map", second_level_mapping );
map.put( "version", new Long( version ));
map.put( "az_version", new Long( az_version ));
ImportExportUtils.exportFloat( map, "rank_bias", rank_bias );
if ( !generic ){
map.put( "id", new Long( id ));
map.put( "last_updated", new Long( last_updated ));
map.put( "selected", new Long( selection_state ));
ImportExportUtils.exportBoolean( map, "select_rec", selection_state_recorded );
ImportExportUtils.exportFloat( map, "pref_count", preferred_count );
map.put( "uid", uid );
}
if ( update_url != null ){
ImportExportUtils.exportString( map, "update_url", update_url );
}
if ( update_check_default_secs != DEFAULT_UPDATE_CHECK_SECS ){
map.put( "update_url_check_secs", new Long( update_check_default_secs ));
}
}
// json constructor
protected
EngineImpl(
MetaSearchImpl meta_search,
int type,
long id,
long last_updated,
float rank_bias,
String name,
JSONObject map )
throws IOException
{
this( meta_search, type, id, last_updated, rank_bias, name );
first_level_mapping = importJSONMappings( map, "value_map", true );
second_level_mapping = importJSONMappings( map, "ctype_map", false );
version = (int)ImportExportUtils.importLong( map, "version", 1 );
az_version = (int)ImportExportUtils.importLong( map, "az_version", AZ_VERSION );
if ( az_version > AZ_VERSION ){
throw( new IOException( MessageText.getString( "metasearch.template.version.bad", new String[]{ name })));
}
String uid_str = (String)map.get( "uid" );
if ( uid_str == null ){
allocateUID( id );
}else{
uid = Base32.decode( uid_str );
}
update_url = ImportExportUtils.importString( map, "update_url" );
update_check_default_secs = (int)ImportExportUtils.importLong( map, "update_url_check_secs", DEFAULT_UPDATE_CHECK_SECS );
}
// json export
protected void
exportToJSONObject(
JSONObject res )
throws IOException
{
exportJSONMappings( res, "value_map", first_level_mapping, true );
exportJSONMappings( res, "ctype_map", second_level_mapping, false );
res.put( "version", new Long( version ));
res.put( "az_version", new Long( az_version ));
res.put( "uid", Base32.encode( uid ));
if ( update_url != null ){
ImportExportUtils.exportJSONString( res, "update_url", update_url );
}
res.put( "update_url_check_secs", new Long( update_check_default_secs ));
}
protected List
importJSONMappings(
JSONObject map,
String str,
boolean level_1 )
throws IOException
{
List result = new ArrayList();
JSONObject field_map = (JSONObject)map.get( str );
if ( field_map != null ){
Iterator it = field_map.entrySet().iterator();
while( it.hasNext()){
Map.Entry entry = (Map.Entry)it.next();
String key = (String)entry.getKey();
List mappings = (List)entry.getValue();
// limited support for the moment:
// level one always maps to same field
// level two always maps to content type
int from_field = vuzeFieldToID( key );
if ( from_field == -1 ){
log( "Unrecognised remapping key '" + key + "'" );
continue;
}
int to_field = level_1?from_field:FIELD_CONTENT_TYPE;
List frs_l = new ArrayList();
for (int i=0;i<mappings.size();i++){
JSONObject mapping = (JSONObject)mappings.get(i);
String from_str = URLDecoder.decode((String)mapping.get( level_1?"from_string":"cat_string" ),"UTF-8");
if ( from_str == null ){
log( "'from' value missing in " + mapping );
continue;
}
from_str = URLDecoder.decode( from_str, "UTF-8" );
String to_str = URLDecoder.decode((String)mapping.get( level_1?"to_string":"media_type" ),"UTF-8");
if ( to_str == null ){
log( "'to' value missing in " + mapping );
continue;
}
frs_l.add( new FieldRemapping( from_str, to_str ));
}
FieldRemapping[] frs = (FieldRemapping[])frs_l.toArray( new FieldRemapping[frs_l.size()]);
result.add( new FieldRemapper( from_field, to_field, frs ));
}
}
return( result );
}
protected void
exportJSONMappings(
JSONObject res,
String str,
List l,
boolean level_1 )
{
JSONObject field_map = new JSONObject();
res.put( str, field_map );
for (int i=0;i<l.size();i++){
FieldRemapper remapper = (FieldRemapper)l.get(i);
int from_field = remapper.getInField();
//int to_field = remapper.getOutField();
String from_field_str = vuzeIDToField( from_field );
JSONArray mappings = new JSONArray();
field_map.put( from_field_str, mappings );
FieldRemapping[] frs = remapper.getMappings();
for (int j=0;j<frs.length;j++){
FieldRemapping fr = frs[j];
String from_str = UrlUtils.encode( fr.getMatchString());
String to_str = fr.getReplacement();
JSONObject map = new JSONObject();
mappings.add( map );
map.put( level_1?"from_string":"cat_string", from_str );
map.put( level_1?"to_string":"media_type", to_str );
}
}
}
protected List
importBEncodedMappings(
Map map,
String name )
throws IOException
{
List result = new ArrayList();
List l = (List)map.get(name);
if ( l != null ){
for (int i=0;i<l.size();i++){
Map entry = (Map)l.get(i);
int from_field = ((Long)entry.get( "from" )).intValue();
int to_field = ((Long)entry.get( "to" )).intValue();
List l2 = (List)entry.get( "maps" );
FieldRemapping[] mappings = new FieldRemapping[ l2.size() ];
for (int j=0;j<mappings.length;j++){
Map entry2 = (Map)l2.get(j);
String from_str = ImportExportUtils.importString( entry2, "from" );
String to_str = ImportExportUtils.importString( entry2, "to" );
mappings[j] = new FieldRemapping( from_str, to_str );
}
result.add( new FieldRemapper( from_field, to_field, mappings ));
}
}
return( result );
}
protected void
exportBEncodedMappings(
Map map,
String name,
List mappings )
throws IOException
{
List l = new ArrayList();
map.put( name, l );
for ( int i=0;i<mappings.size();i++){
FieldRemapper mapper = (FieldRemapper)mappings.get(i);
Map m = new HashMap();
l.add( m );
m.put( "from", new Long( mapper.getInField()));
m.put( "to", new Long( mapper.getOutField()));
List l2 = new ArrayList();
m.put( "maps", l2 );
FieldRemapping[] frs = mapper.getMappings();
for (int j=0;j<frs.length;j++){
FieldRemapping fr = frs[j];
Map m2 = new HashMap();
l2.add( m2 );
ImportExportUtils.exportString( m2, "from", fr.getMatchString());
ImportExportUtils.exportString( m2, "to", fr.getReplacement());
}
}
}
public String
exportToJSONString()
throws IOException
{
JSONObject obj = new JSONObject();
exportToJSONObject( obj );
return( obj.toString());
}
public int
getAZVersion()
{
return( az_version );
}
public int
getVersion()
{
return( version );
}
protected void
setVersion(
int _v )
{
version = _v;
}
public String
getUID()
{
return( Base32.encode( uid ));
}
protected void
setUID(
String str )
{
uid = Base32.decode( str );
}
protected void
allocateUID(
long id )
{
uid = new byte[10];
if ( id >= 0 && id < Integer.MAX_VALUE ){
// id is a vuze one, derive uid from it as already unique
uid[0] = (byte)(id>>24);
uid[1] = (byte)(id>>16);
uid[2] = (byte)(id>>8);
uid[3] = (byte)id;
}else{
RandomUtils.nextSecureBytes( uid );
configDirty();
}
}
public boolean
sameLogicAs(
Engine other )
{
try{
Map m1 = exportToBencodedMap();
Map m2 = other.exportToBencodedMap();
String[] to_remove =
{ "type",
"id",
"last_updated",
"selected",
"select_rec",
"source",
"rank_bias",
"pref_count",
"version",
"az_version",
"uid" };
for (int i=0;i<to_remove.length;i++){
m1.remove( to_remove[i] );
m2.remove( to_remove[i] );
}
return( BEncoder.mapsAreIdentical( m1, m2 ));
}catch( Throwable e ){
Debug.printStackTrace(e);
return( false );
}
}
public Result[]
search(
SearchParameter[] params,
Map context,
int desired_max_matches,
int absolute_max_matches,
String headers,
final ResultListener listener )
throws SearchException
{
if ( context == null ){
context = new HashMap();
}
try{
final Set<Result> results_informed = new HashSet<Result>();
final boolean[] complete_informed = { false };
ResultListener interceptor =
new ResultListener()
{
public void
contentReceived(
Engine engine,
String content )
{
listener.contentReceived(engine, content);
}
public void
matchFound(
Engine engine,
String[] fields )
{
listener.matchFound(engine, fields);
}
public void
resultsReceived(
Engine engine,
Result[] results )
{
listener.resultsReceived(engine, results);
synchronized( results_informed ){
results_informed.addAll( Arrays.asList( results ));
}
}
public void
resultsComplete(
Engine engine)
{
listener.resultsComplete(engine);
synchronized( results_informed ){
complete_informed[0] = true;
}
}
public void
engineFailed(
Engine engine,
Throwable cause )
{
listener.engineFailed(engine, cause);
}
public void
engineRequiresLogin(
Engine engine,
Throwable cause )
{
listener.engineRequiresLogin(engine, cause);
}
};
Result[] results = searchAndMap( params, context, desired_max_matches, absolute_max_matches, headers, listener==null?null:interceptor );
if ( listener != null ){
boolean inform_complete;
List<Result> inform_result = new ArrayList<Result>();
synchronized( results_informed ){
for ( Result r: results ){
if ( !results_informed.contains( r )){
inform_result.add( r );
}
}
inform_complete = !complete_informed[0];
}
if ( inform_result.size() > 0 ){
listener.resultsReceived( this, inform_result.toArray( new Result[ inform_result.size()] ));
}
if ( inform_complete ){
listener.resultsComplete( this );
}
}
return( results );
}catch( Throwable e ){
if ( e instanceof SearchLoginException ){
if ( listener != null ){
listener.engineRequiresLogin(this, e);
}
throw((SearchLoginException)e);
}else if ( e instanceof SearchException ){
if ( listener != null ){
listener.engineFailed( this, e);
}
throw((SearchException)e);
}else{
if ( listener != null ){
listener.engineFailed( this, e);
}
throw( new SearchException( "Search failed", e ));
}
}
}
protected Result[]
searchAndMap(
SearchParameter[] params,
Map context,
int desired_max_matches,
int absolute_max_matches,
String headers,
final ResultListener listener )
throws SearchException
{
// default values
context.put( Engine.SC_AZID, ConstantsVuze.AZID );
if ( context.get( Engine.SC_SOURCE ) == null ){
context.put( Engine.SC_SOURCE, "search" );
}
Result[] results =
searchSupport(
params,
context,
desired_max_matches,
absolute_max_matches,
headers,
new ResultListener()
{
public void
contentReceived(
Engine engine,
String content )
{
if ( listener != null ){
listener.contentReceived(engine, content);
}
}
public void
matchFound(
Engine engine,
String[] fields )
{
if ( listener != null ){
listener.matchFound(engine, fields);
}
}
public void
resultsReceived(
Engine engine,
Result[] results)
{
if ( listener != null ){
listener.resultsReceived(engine, mapResults( results ));
}
}
public void
resultsComplete(
Engine engine )
{
if ( listener != null ){
listener.resultsComplete(engine);
}
}
public void
engineFailed(
Engine engine,
Throwable cause )
{
log( "Search failed", cause );
if ( listener != null ){
listener.engineFailed(engine, cause);
}
}
public void
engineRequiresLogin(
Engine engine,
Throwable cause )
{
log( "Search requires login", cause );
if ( listener != null ){
listener.engineRequiresLogin(engine, cause);
}
}
});
return( mapResults( results ));
}
protected Result[]
mapResults(
Result[] results )
{
for (int i=0;i<results.length;i++){
Result result = results[i];
for (int j=0;j<first_level_mapping.size();j++){
FieldRemapper mapper = (FieldRemapper)first_level_mapping.get(j);
mapper.remap( result );
}
for (int j=0;j<second_level_mapping.size();j++){
FieldRemapper mapper = (FieldRemapper)second_level_mapping.get(j);
mapper.remap( result );
}
}
return( results );
}
protected abstract Result[]
searchSupport(
SearchParameter[] params,
Map searchContext,
int desired_max_matches,
int absolute_max_matches,
String headers,
ResultListener listener )
throws SearchException;
public void
delete()
{
meta_search.removeEngine( this );
}
protected MetaSearchImpl
getMetaSearch()
{
return( meta_search );
}
protected int
vuzeFieldToID(
String field )
{
for (int i=0;i<FIELD_NAMES.length;i++){
if ( field.equalsIgnoreCase( FIELD_NAMES[i] )){
return( FIELD_IDS[i]);
}
}
return( -1 );
}
protected String
vuzeIDToField(
int id )
{
for (int i=0;i<FIELD_IDS.length;i++){
if ( id == FIELD_IDS[i] ){
return( FIELD_NAMES[i]);
}
}
return( null );
}
public int
getType()
{
return( type );
}
protected void
setId(
long _id )
{
id = _id;
}
public long
getId()
{
return id;
}
public long
getLastUpdated()
{
return( last_updated );
}
public String
getName()
{
return( name );
}
protected void
setName(
String n )
{
name = n;
}
public boolean
isActive()
{
return( getSelectionState() != SEL_STATE_DESELECTED );
}
public boolean
isPublic()
{
return( is_public );
}
protected void
setPublic(
boolean p )
{
is_public = p;
}
public int
getSelectionState()
{
return( selection_state );
}
public void
setSelectionState(
int state )
{
if ( state != selection_state ){
// only record transitions to or from manual selection for non-local templates
if ( getSource() == ENGINE_SOURCE_VUZE ){
if ( state == SEL_STATE_MANUAL_SELECTED ||
selection_state == SEL_STATE_MANUAL_SELECTED ){
selection_state_recorded = false;
checkSelectionStateRecorded();
}
}
selection_state = state;
configDirty();
}
}
public boolean
isAuthenticated()
{
return( false );
}
public void
recordSelectionState()
{
selection_state_recorded = false;
checkSelectionStateRecorded();
}
public void
checkSelectionStateRecorded()
{
if ( !selection_state_recorded ){
try{
boolean selected = selection_state != SEL_STATE_DESELECTED;
log( "Marking template id " + getId() + " as selected=" + selected );
PlatformMetaSearchMessenger.setTemplatetSelected( meta_search.getManager().getExtensionKey(), getId(), ConstantsVuze.AZID, selected);
selection_state_recorded = true;
}catch( Throwable e ){
log( "Failed to record selection state", e );
}
}
}
public int
getSource()
{
return( source );
}
public void
setSource(
int _source )
{
if ( source != _source ){
source = _source;
configDirty();
}
}
public float
getRankBias()
{
return( rank_bias );
}
public void
setRankBias(
float _rank_bias )
{
if ( rank_bias != _rank_bias ){
rank_bias = _rank_bias;
configDirty();
}
}
public void
setPreferredDelta(
float delta )
{
float new_pref = preferred_count + delta;
new_pref = Math.max( 0, new_pref );
new_pref = Math.min( 10, new_pref );
if ( new_pref != preferred_count ){
preferred_count = new_pref;
configDirty();
}
}
public float
getPreferredWeighting()
{
return( preferred_count );
}
public float
applyRankBias(
float _rank )
{
float rank = _rank*rank_bias;
rank *= (1 + 0.025 * preferred_count );
rank = Math.min( rank, 1.0f );
rank = Math.max( rank, 0.0f );
// System.out.println( getName() + " (" + rank_bias+"/"+preferred_count + "): " + _rank + " -> " + rank );
return( rank );
}
public boolean
isMine()
{
return( getLocalBoolean( LD_CREATED_BY_ME, false ));
}
public void
setMine(
boolean mine )
{
setLocalBoolean( LD_CREATED_BY_ME, mine );
}
protected String
getUpdateURL()
{
return( update_url );
}
protected void
setUpdateURL(
String url )
{
update_url = url;
}
protected int
getUpdateCheckSecs()
{
long l = getLocalLong( LD_UPDATE_CHECK_SECS, 0 );
if ( l != 0 ){
return((int)l);
}
return( update_check_default_secs );
}
protected void
setDefaultUpdateCheckSecs(
int secs )
{
update_check_default_secs = secs;
}
protected void
setLocalUpdateCheckSecs(
int secs )
{
setLocalLong( LD_UPDATE_CHECK_SECS, secs );
}
protected long
getLastUpdateCheck()
{
return( getLocalLong( LD_LAST_UPDATE_CHECK, 0 ));
}
protected void
setLastUpdateCheck(
long when )
{
setLocalLong( LD_LAST_UPDATE_CHECK, when );
}
public int
getAutoDownloadSupported()
{
// default impl based on availability of fields that permit auto-dl
// overridden in RSS engine ( for example ) to be based on explicit
// channel tag
boolean ok =
supportsField( Engine.FIELD_TORRENTLINK ) ||
supportsField( Engine.FIELD_DOWNLOADBTNLINK );
return( ok?AUTO_DL_SUPPORTED_YES:AUTO_DL_SUPPORTED_NO );
}
protected void
configDirty()
{
if ( meta_search != null ){
meta_search.configDirty();
}
}
public void
addPotentialAssociation(
String key )
{
meta_search.addPotentialAssociation( this, key );
}
public void
exportToVuzeFile(
File target )
throws IOException
{
VuzeFile vf = VuzeFileHandler.getSingleton().create();
vf.addComponent(
VuzeFileComponent.COMP_TYPE_METASEARCH_TEMPLATE,
exportToBencodedMap());
vf.write( target );
}
public VuzeFile
exportToVuzeFile()
throws IOException
{
return( exportToVuzeFile( false ));
}
public VuzeFile
exportToVuzeFile(
boolean generic )
throws IOException
{
VuzeFile vf = VuzeFileHandler.getSingleton().create();
vf.addComponent(
VuzeFileComponent.COMP_TYPE_METASEARCH_TEMPLATE,
exportToBencodedMap( generic ));
return( vf );
}
private String
getLocalKey()
{
return( "metasearch.engine." + id + ".local" );
}
public void
reset()
{
synchronized( this ){
Map map = COConfigurationManager.getMapParameter( getLocalKey(), new HashMap());
map.remove( LD_COOKIES );
map.remove( LD_ETAG );
map.remove( LD_LAST_MODIFIED );
COConfigurationManager.setParameter( getLocalKey(), map );
}
}
protected void
setLocalString(
String key,
String value )
{
synchronized( this ){
String existing = getLocalString( key );
if ( existing != null && value != null && existing.equals( value )){
return;
}
Map map = COConfigurationManager.getMapParameter( getLocalKey(), new HashMap());
try{
ImportExportUtils.exportString(map, key, value);
COConfigurationManager.setParameter( getLocalKey(), map );
}catch( Throwable e ){
Debug.printStackTrace( e );
}
}
}
protected String
getLocalString(
String key )
{
synchronized( this ){
Map map = COConfigurationManager.getMapParameter( getLocalKey(), new HashMap());
try{
return( ImportExportUtils.importString( map, key ));
}catch( Throwable e ){
Debug.printStackTrace( e );
return( null );
}
}
}
protected void
setLocalBoolean(
String key,
boolean value )
{
setLocalLong( key, value?1:0 );
}
protected boolean
getLocalBoolean(
String key,
boolean def )
{
return( getLocalLong( key, def?1:0 ) == 1 );
}
protected void
setLocalLong(
String key,
long value )
{
synchronized( this ){
long existing = getLocalLong( key, 0 );
if ( existing == value ){
return;
}
Map map = COConfigurationManager.getMapParameter( getLocalKey(), new HashMap());
try{
map.put( key, new Long( value));
COConfigurationManager.setParameter( getLocalKey(), map );
}catch( Throwable e ){
Debug.printStackTrace( e );
}
}
}
protected long
getLocalLong(
String key,
long def )
{
synchronized( this ){
Map map = COConfigurationManager.getMapParameter( getLocalKey(), new HashMap());
try{
return( ImportExportUtils.importLong( map, key, def ));
}catch( Throwable e ){
Debug.printStackTrace( e );
return( def );
}
}
}
protected void
setUserData(
Object key,
Object value )
{
synchronized( this ){
if ( user_data == null ){
if ( key == null ){
return;
}
user_data = new HashMap(4);
}
if ( key == null ){
user_data.remove( key );
if ( user_data.size() == 0 ){
user_data = null;
}
}else{
user_data.put( key, value );
}
}
}
protected Object
getUserData(
Object key )
{
synchronized( this ){
if ( user_data == null ){
return( null );
}
return( user_data.get( key ));
}
}
protected File
getDebugFile()
{
if ( logging_enabled ){
return( new File( AEDiagnostics.getLogDir(), "MetaSearch_Engine_" + getId() + ".txt" ));
}else{
return( null );
}
}
protected synchronized void
debugStart()
{
File f = getDebugFile();
if ( f != null ){
f.delete();
}
}
protected synchronized void
debugLog(
String str )
{
File f = getDebugFile();
if ( f != null ){
PrintWriter pw = null;
try{
pw = new PrintWriter(new FileWriter( f, true ));
pw.println( str );
}catch( Throwable e ){
}finally{
if ( pw != null ){
pw.close();
}
}
}
}
protected void
log(
String str )
{
if ( meta_search != null ){
meta_search.log( "Engine " + getId() + ": " + str );
}
}
protected void
log(
String str,
Throwable e )
{
if ( meta_search != null ){
meta_search.log( "Engine " + getId() + ": " + str, e );
}
}
public String
getString()
{
return( "id=" + getId() + ", name=" + getName() + ", source=" + ENGINE_SOURCE_STRS[getSource()] + ", selected=" + SEL_STATE_STRINGS[getSelectionState()] + ", rb=" + rank_bias + ", pref=" + preferred_count );
}
public String
getString(
boolean full )
{
return( getString());
}
}