// Copyright (C) 2010, 2011 Zeno Gantner, Chris Newell
//
// This file is part of MyMediaLite.
//
// MyMediaLite 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 3 of the License, or
// (at your option) any later version.
//
// MyMediaLite 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 MyMediaLite. If not, see <http://www.gnu.org/licenses/>.
package org.mymedialite.util;
import java.util.HashMap;
import java.util.Map.Entry;
import java.lang.reflect.Field;
import org.mymedialite.IItemAttributeAwareRecommender;
import org.mymedialite.IItemRelationAwareRecommender;
import org.mymedialite.IRecommender;
import org.mymedialite.IUserAttributeAwareRecommender;
import org.mymedialite.IUserRelationAwareRecommender;
import org.mymedialite.itemrec.ItemRecommender;
import org.mymedialite.ratingprediction.RatingPredictor;
/**
* Helper class with utility methods for handling recommenders.
*
* Contains methods for creating and configuring recommender objects, as well as listing recommender classes.
* @version 2.03
*/
public class Recommender {
// Prevent instantiation.
private Recommender() {}
static String normalizeName(String s) {
s = s.replaceAll("_", "");
return s.toUpperCase();
}
public interface ErrorHandler {
void reportError(String error);
}
public static class DefaultErrorHandler implements ErrorHandler {
public void reportError(String message) {
System.err.println(message);
}
}
/**
* Configure a recommender.
* @param recommender the recommender to configure
* @param parameters a string containing the parameters as key-value pairs
* @param errorHandler interface for error reporting
* @return the configured recommender
*/
public static <T> T configure(T recommender, String parameters, ErrorHandler errorHandler) throws IllegalAccessException {
RecommenderParameters parameters_dictionary = new RecommenderParameters(parameters);
return configure(recommender, parameters_dictionary, errorHandler);
}
/**
* Configure a recommender.
* @param recommender the recommender to configure
* @param parameters a string containing the parameters as key-value pairs
*/
public static <T> T configure(T recommender, String parameters) throws IllegalAccessException {
return configure(recommender, parameters, new DefaultErrorHandler());
}
/**
* Configure a recommender.
* @param recommender the recommender to configure
* @param parameters a dictionary containing the parameters as key-value pairs
* @param errorHandler interface for error reporting
* @return the configured recommender
*/
public static <T> T configure(T recommender, HashMap<String, String> parameters, ErrorHandler errorHandler) throws IllegalAccessException {
for (Entry<String, String> entry : parameters.entrySet()) {
String key = normalizeName(entry.getKey());
// TODO Invoke setters when available.
for (Field field : recommender.getClass().getFields()) {
if (field.getName().equalsIgnoreCase(key)) {
//System.out.println("key: " + key + " value:" + entry.getValue());
if (field.getType().getName().equals("double")) {
field.set(recommender, Double.parseDouble(entry.getValue()));
} else if (field.getType().getName().equals("float")) {
field.set(recommender, Float.parseFloat(entry.getValue()));
} else if (field.getType().getName().equals("int")) {
field.set(recommender, Integer.parseInt(entry.getValue()));
} else if (field.getType().getName().equals("boolean")) {
field.set(recommender, Boolean.parseBoolean(entry.getValue()));
} else {
errorHandler.reportError("Parameter " + key + " has unknown type " + field.getType());
}
}
}
}
return recommender;
}
/**
* Sets a property of a MyMediaLite recommender.
* @param recommender An <see cref="IRecommender"/>
* @param key the name of the property (case insensitive)
* @param val the string representation of the value
*/
public static void setProperty(IRecommender recommender, String key, String val) throws IllegalAccessException {
key = normalizeName(key);
// TODO Invoke setter when available.
for (Field field : recommender.getClass().getFields()) {
if (field.getName().equalsIgnoreCase(key)) {
if (field.getType().getName().equals("double")) {
field.set(recommender, Double.parseDouble(val));
} else if (field.getType().getName().equals("float")) {
field.set(recommender, Float.parseFloat(val));
} else if (field.getType().getName().equals("int")) {
field.set(recommender, Integer.parseInt(val));
} else if (field.getType().getName().equals("boolean")) {
field.set(recommender, Boolean.parseBoolean(val));
} else {
throw new IllegalArgumentException("Parameter " + key + " has unknown type " + field.getType());
}
}
}
}
/**
* Create a rating predictor from the type name.
* @param typename a string containing the type name
* @return an item recommender object of type typename if the recommender type is found, null otherwise
*/
public static RatingPredictor createRatingPredictor(String typename) {
if (!typename.startsWith("org.mymedialite.ratingprediction.")) typename = "org.mymedialite.ratingprediction." + typename;
try {
return (RatingPredictor) Class.forName(typename).getConstructor().newInstance();
} catch (ClassCastException e) {
System.err.println(typename + " is not a subclass of org.mymedialite.ratingprediction.RatingPredictor");
} catch (Exception e) {
System.err.println("Unable to instantiate " + typename);
}
return null;
}
/**
* Create an item recommender from the type name.
* @param typename a string containing the type name
* @return an item recommender object of type typename if the recommender type is found, null otherwise
*/
public static ItemRecommender createItemRecommender(String typename) {
if (!typename.startsWith("org.mymedialite.itemrec.")) typename = "org.mymedialite.itemrec." + typename;
try {
return (ItemRecommender) Class.forName(typename).getConstructor().newInstance();
} catch (ClassCastException e) {
System.err.println(typename + " is not a subclass of org.mymedialite.itemrec.ItemRecommender");
return null;
} catch (Exception e) {
System.err.println("Unable to instantiate " + typename);
}
return null;
}
/**
* Describes the kind of data needed by this recommender.
* @param recommender a recommender
* @return a string containing the additional datafiles needed for training this recommender
*/
public static String needs(IRecommender recommender) {
// determine necessary data
String needs = "";
if (recommender instanceof IUserRelationAwareRecommender)
needs += "--user-relations=FILE, ";
if (recommender instanceof IItemRelationAwareRecommender)
needs += "--item-relations=FILE, ";
if (recommender instanceof IUserAttributeAwareRecommender)
needs += "--user-attributes=FILE, ";
if (recommender instanceof IItemAttributeAwareRecommender)
needs += "--item-attributes=FILE, ";
return needs.substring(0, Math.max(0, needs.length() - 2));
}
}