/*
* Copyright 2009 Peter Karich, peat_hal 'at' users 'dot' sourceforge 'dot' net.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package de.timefinder.core.io.text;
import de.timefinder.data.algo.DataPoolSettings;
import de.timefinder.algo.constraint.RasterConstraint;
import de.timefinder.core.util.FixRelationShip;
import de.timefinder.data.util.Helper;
import de.timefinder.data.DataPool;
import de.timefinder.data.Event;
import de.timefinder.data.Feature;
import de.timefinder.data.Location;
import de.timefinder.data.Person;
import de.timefinder.data.access.Dao;
import de.timefinder.data.util.MapEntry;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import javolution.util.FastMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Imports tab or comma separated text files.
*
* @author Peter Karich, peat_hal 'at' users 'dot' sourceforge 'dot' net
*/
public class ImportText {
private Log logger = LogFactory.getLog(getClass());
private DataPool dataPool;
private File folder;
private String propFile;
private Properties prop;
private BufferedReader eventReader;
private BufferedReader featureReader;
private BufferedReader locationReader;
private BufferedReader personReader;
private String columnSeparator;
private String idSeparator;
private ObjectParsing objParsing = new ObjectParsing();
private Map<String, Class> aliases;
protected DataPoolSettings settings;
private boolean fixRelationShip;
private Dao<Event> eDao;
protected Map<Class, Map<Long, Object>> simpleDaos = FastMap.newInstance();
public ImportText(DataPool dataPool, DataPoolSettings settings, File folder) {
this.dataPool = dataPool;
eDao = dataPool.getDao(Event.class);
this.settings = settings;
this.folder = folder;
initProperties();
initAliases();
}
public void initProperties() {
propFile = "timefinder.properties";
prop = new Properties();
prop.setProperty("tf.column.separator", "TAB");
prop.setProperty("tf.id.separator", "COMMA");
prop.setProperty("tf.days", "5");
prop.setProperty("tf.timeSlotsPerDay", "10");
prop.setProperty("tf.msPerTimeSlot", "" + 1000 * 60 * 60L);
prop.setProperty("tf.feature.file", "features.txt");
prop.setProperty("tf.person.file", "persons.txt");
prop.setProperty("tf.location.file", "locations.txt");
prop.setProperty("tf.event.file", "events.txt");
prop.setProperty("tf.fixRelationship", "true");
}
protected void readProperties() {
}
public void initAliases() {
aliases = FastMap.newInstance();
aliases.put("events", Event.class);
aliases.put("persons", Person.class);
aliases.put("locations", Location.class);
aliases.put("features", Feature.class);
}
public void doWork() {
logger.info("Start doWork");
readSettings();
initSimpleDao();
initReaders();
readData();
fixRelationShip();
addToDataPool();
logger.info("End doWork");
}
public boolean isFixRelationShip() {
return fixRelationShip;
}
public void setFixRelationShip(boolean fixRelationShip) {
this.fixRelationShip = fixRelationShip;
}
public void setEventReader(Reader reader) {
eventReader = new BufferedReader(reader);
}
public void setLocationReader(Reader reader) {
locationReader = new BufferedReader(reader);
}
public void setFeatureReader(Reader reader) {
featureReader = new BufferedReader(reader);
}
public void setPersonReader(Reader reader) {
personReader = new BufferedReader(reader);
}
public Properties getProperties() {
return prop;
}
public String propToString(String property) {
if ("TAB".equalsIgnoreCase(property))
return "\t";
else if ("COMMA".equalsIgnoreCase(property) || ",".equalsIgnoreCase(property))
return ",";
else if ("RETURN".equalsIgnoreCase(property))
return "\n";
else
return property;
}
public DataPool getDataPool() {
return dataPool;
}
public File getFolder() {
return folder;
}
protected void readFile(BufferedReader reader, Class clazz) throws
IOException, InstantiationException, IllegalAccessException {
if (reader == null)
return;
String firstLine = reader.readLine();
if (firstLine == null)
return;
if (firstLine.length() == 0) {
logger.fatal("The first line should define the property names and should start with #"
+ "\n for class: " + clazz);
return;
}
Map<String, Method> setterMethods = FastMap.newInstance();
Helper.fillSettersAndGetters(clazz, setterMethods, null);
firstLine = firstLine.substring(1).trim();
List<Entry<Method, Parsing>> columns = new ArrayList();
Parsing idParsing = null;
int idColumn = -1;
int counter = 0;
for (String column : firstLine.split(columnSeparator)) {
column = column.trim();
Method method = setterMethods.get(column);
if (method == null)
continue;
Class[] params = method.getParameterTypes();
if (params.length != 1) {
logger.warn("Setter method " + method.getName() + " should have one parameter!");
continue;
}
Parsing parsing = objParsing.getParsing(params[0]);
if (parsing == null) {
if (Collection.class.isAssignableFrom(params[0])) {
Class innerType = aliases.get(column);
if (innerType != null)
parsing = new TextCollectionParsing(idSeparator, simpleDaos.get(innerType), innerType);
}
}
if (parsing == null) {
columns.add(null);
logger.warn("Cannot find setter for property: " + column);
} else {
if ("id".equalsIgnoreCase(column)) {
idParsing = parsing;
idColumn = counter;
}
columns.add(new MapEntry(method, parsing));
}
counter++;
}
if (columns.size() == 0) {
logger.fatal("Couldn't find property definition for: " + clazz);
return;
}
if (idParsing == null || idColumn < 0) {
logger.fatal("Couldn't find id property in the first line (property definition) for: " + clazz);
return;
}
Map<Long, Object> dao = simpleDaos.get(clazz);
String line;
while ((line = reader.readLine()) != null) {
if (line.startsWith("#"))
continue;
counter = 0;
String[] cols = line.split(columnSeparator);
Long id = (Long) idParsing.parse(cols[idColumn]);
Object obj = dao.get(id);
if (obj == null) {
obj = clazz.newInstance();
dao.put(id, obj);
}
for (int ii = 0; ii < cols.length; ii++, counter++) {
String columnContent = cols[ii].trim();
if (counter >= columns.size()) {
logger.warn("Cannot parse column " + (counter + 1) + " with content:" + columnContent + "\n because only "
+ columns.size() + " properties were specified in header: " + columns);
break;
}
Entry<Method, Parsing> propertyEntry = columns.get(counter);
if (propertyEntry == null)
continue;
Object property = propertyEntry.getValue().parse(columnContent);
try {
propertyEntry.getKey().invoke(obj, property);
} catch (InvocationTargetException ex) {
logger.warn("Cannot set parameter: " + propertyEntry.getKey().getName(), ex);
}
} // iterate through every column
} // iterate through every line of the text file
}
protected void readSettings() {
try {
FileReader propReader = new FileReader(new File(folder, propFile));
prop.load(propReader);
} catch (FileNotFoundException ex) {
logger.info("File not found: " + propFile + " - Will apply default properties:" + prop);
} catch (IOException ex) {
logger.fatal("Couldn't load properties from " + propFile);
}
columnSeparator = propToString(prop.getProperty("tf.column.separator"));
idSeparator = propToString(prop.getProperty("tf.id.separator"));
try {
settings.setNumberOfDays(Integer.parseInt(prop.getProperty("tf.days")));
} catch (Exception ex) {
logger.warn("Couldn't change number of days: " + prop.getProperty("tf.days"));
}
try {
settings.setTimeslotsPerDay(Integer.parseInt(prop.getProperty("tf.timeSlotsPerDay")));
} catch (Exception ex) {
logger.warn("Couldn't change time-slots per day: " + prop.getProperty("tf.timeSlotsPerDay"));
}
try {
settings.setMillisPerTimeslot(Long.parseLong(prop.getProperty("tf.msPerTimeSlot")));
} catch (Exception ex) {
logger.warn("Couldn't change ms / time-slot: " + prop.getProperty("tf.msPerTimeSlot"));
}
}
public void fixRelationShip() {
try {
fixRelationShip = true;
fixRelationShip = Boolean.parseBoolean(prop.getProperty("tf.fixRelationship"));
} catch (Exception ex) {
logger.warn("Couldn't change fixRelationship: " + prop.getProperty("tf.fixRelationship"));
}
if (fixRelationShip) {
int counter = new FixRelationShip(dataPool).doWork();
if (counter > 1)
logger.warn("Had to fix " + counter + " relationships");
}
}
protected void initReaders() {
String fFile = prop.getProperty("tf.feature.file");
try {
if (featureReader == null)
setFeatureReader(new FileReader(new File(folder, fFile)));
} catch (FileNotFoundException ex) {
logger.fatal("Couldn't find " + fFile);
}
String eFile = prop.getProperty("tf.event.file");
try {
if (eventReader == null)
setEventReader(new FileReader(new File(folder, eFile)));
} catch (FileNotFoundException ex) {
logger.fatal("Couldn't find " + eFile);
}
String pFile = prop.getProperty("tf.person.file");
try {
if (personReader == null)
setPersonReader(new FileReader(new File(folder, pFile)));
} catch (FileNotFoundException ex) {
logger.fatal("Couldn't find " + pFile);
}
String lFile = prop.getProperty("tf.location.file");
try {
if (locationReader == null)
setLocationReader(new FileReader(new File(folder, lFile)));
} catch (FileNotFoundException ex) {
logger.fatal("Couldn't find " + lFile);
}
}
protected void readData() {
try {
readFile(eventReader, Event.class);
} catch (Exception ex) {
logger.fatal("Failed to parse event file!", ex);
}
try {
readFile(personReader, Person.class);
} catch (Exception ex) {
logger.fatal("Failed to parse person file!", ex);
}
try {
readFile(featureReader, Feature.class);
} catch (Exception ex) {
logger.fatal("Failed to parse feature file!", ex);
}
try {
readFile(locationReader, Location.class);
} catch (Exception ex) {
logger.fatal("Failed to parse location file!", ex);
}
}
public void initSimpleDao() {
for (Dao dao : dataPool.getDaos()) {
simpleDaos.put(dao.getType(), new FastMap<Long, Object>());
}
}
public void addToDataPool() {
for (Entry<Class, Map<Long, Object>> sDao : simpleDaos.entrySet()) {
Dao tmpDao = dataPool.getDao(sDao.getKey());
if (tmpDao == null) {
logger.warn("Dao for " + sDao.getKey() + " not found in data pool!?");
continue;
}
tmpDao.attachAll(sDao.getValue().values());
}
}
protected Event newEvent() {
Event ev = eDao.create();
ev.putConstraint(new RasterConstraint(settings.createWeekRaster()));
return ev;
}
}