es.ucm.fdi.gaia.jcolibri.connector.PlainTextConnector Maven / Gradle / Ivy
Show all versions of jCOLIBRI Show documentation
package es.ucm.fdi.gaia.jcolibri.connector;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.StringTokenizer;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.logging.log4j.LogManager;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import es.ucm.fdi.gaia.jcolibri.cbrcore.Attribute;
import es.ucm.fdi.gaia.jcolibri.cbrcore.CBRCase;
import es.ucm.fdi.gaia.jcolibri.cbrcore.CaseBaseFilter;
import es.ucm.fdi.gaia.jcolibri.cbrcore.CaseComponent;
import es.ucm.fdi.gaia.jcolibri.cbrcore.Connector;
import es.ucm.fdi.gaia.jcolibri.connector.plaintextutils.PlainTextTypeConverter;
import es.ucm.fdi.gaia.jcolibri.exception.AttributeAccessException;
import es.ucm.fdi.gaia.jcolibri.exception.InitializingException;
import es.ucm.fdi.gaia.jcolibri.util.FileIO;
/**
*
* Implements a generic PlainText Connector.
*
* It manages the persistence of the cases automatically into textual files. Features:
*
* - By default it only can manage a few data types, although developers can add
* their own ones implementing the TypeAdaptor interface.
* Supported types and the type extension mechanism is explained in PlainTextTypeConverter.
* - Only works with one file.
*
*
* This connector uses the property in the initFromXMLfile() parameter to obtain the
* configuration file. This file is a xml that follows the Schema defined in
* /doc/configfilesSchemas/PlainTextConnector.xsd:
*
*
* This class does not implement any cache mechanims, so cases are read and
* written directly. This can be very inefficient in some operations (mainly in
* reading)
*
* Some methods will fail when executing the connector with a case base file inside a jar file.
* The retrieve() methods will work properly but the methods that write in the file will fail.
* Extract the file to the file system and run the connector with that location to solve these problems.
*
* For an example see Test6.
*
* @author Juan Antonio Recio García
* @version 2.0
* @see PlainTextTypeConverter
* @see TypeAdaptor
*/
public class PlainTextConnector implements Connector {
/* Text file path. */
protected String PROP_FILEPATH = "";
/* Columns separator. */
protected String PROP_DELIM = "";
private Class extends CaseComponent> descriptionClass;
private Class extends CaseComponent> solutionClass;
private Class extends CaseComponent> justOfSolutionClass;
private Class extends CaseComponent> resultClass;
List descriptionMaps;
List solutionMaps;
List justOfSolutionMaps;
List resultMaps;
public void initFromXMLfile(URL file) throws InitializingException {
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(file.openStream());
/** File containing cases */
this.PROP_FILEPATH = doc.getElementsByTagName("FilePath").item(0).getTextContent();
/** Text separator */
this.PROP_DELIM = doc.getElementsByTagName("Delimiters").item(0).getTextContent();
/** classes that compose the case*/
this.descriptionClass = Class.forName(doc.getElementsByTagName("DescriptionClassName").item(0).getTextContent()).asSubclass(CaseComponent.class);
try{
this.solutionClass = Class.forName(doc.getElementsByTagName("SolutionClassName").item(0).getTextContent()).asSubclass(CaseComponent.class);
}catch(Exception e) {}
try{
this.justOfSolutionClass = Class.forName(doc.getElementsByTagName("JustificationOfSolutionClassName").item(0).getTextContent()).asSubclass(CaseComponent.class);
}catch(Exception e) {}
try{
this.resultClass = Class.forName(doc.getElementsByTagName("ResultClassName").item(0).getTextContent()).asSubclass(CaseComponent.class);
}catch(Exception e) {}
/** Mappings */
this.descriptionMaps = findMaps(doc.getElementsByTagName("DescriptionMappings").item(0), this.descriptionClass);
if(this.solutionClass != null)
this.solutionMaps = findMaps(doc.getElementsByTagName("SolutionMappings").item(0), this.solutionClass);
if(this.justOfSolutionClass != null)
this.justOfSolutionMaps = findMaps(doc.getElementsByTagName("JustificationOfSolutionMappings").item(0), this.justOfSolutionClass);
if(this.resultClass != null)
this.resultMaps = findMaps(doc.getElementsByTagName("ResultMappings").item(0), this.resultClass);
}catch(Exception e){
throw new InitializingException(e);
}
}
private List findMaps(Node n, Class> _class)
{
List res = new ArrayList();
NodeList childs = n.getChildNodes();
for(int i=0; i cases)
{
try {
BufferedWriter br = null;
br = new BufferedWriter(new FileWriter(FileIO.findFile(this.PROP_FILEPATH).getFile(), true));
//if (br == null) throw new Exception("Error opening file for writing: "+ this.PROP_FILEPATH);
char separator = this.PROP_DELIM.charAt(0);
for (CBRCase _case : cases) {
br.newLine();
StringBuffer line = new StringBuffer();
CaseComponent description = _case.getDescription();
writeComponent(description, this.descriptionMaps, line, separator, true);
CaseComponent solution = _case.getSolution();
if(solution!=null)
{
line.append(separator);
writeComponent(solution, this.solutionMaps, line, separator, false);
}
CaseComponent justOfSolution = _case.getJustificationOfSolution();
if(justOfSolution!=null)
{
line.append(separator);
writeComponent(justOfSolution, this.justOfSolutionMaps, line, separator, false);
}
CaseComponent result = _case.getResult();
if(result!=null)
{
line.append(separator);
writeComponent(result, this.resultMaps, line, separator, false);
}
br.write(line.toString());
}
br.close();
} catch (Exception e) {
LogManager.getLogger(this.getClass()).error(e);
}
}
private void writeComponent(CaseComponent comp, List maps, StringBuffer line, char separator, boolean includeId)
{
try {
if(includeId)
line.append(comp.getIdAttribute().getValue(comp));
for(Attribute a: maps)
{
line.append(separator);
line.append(a.getValue(comp));
}
} catch (AttributeAccessException e) {
LogManager.getLogger(this.getClass()).error(e);
}
}
/**
* Deletes cases from the case base. It only uses the case name (primary
* key) to remove the row. Note that this method is very inefficient because
* it reads all the database, removes the rows in memory, and writes it
* again into the text file.
*
* @param cases
* Cases to delete
*/
public void deleteCases(Collection cases){
try {
BufferedReader br = null;
br = new BufferedReader( new InputStreamReader(FileIO.findFile(this.PROP_FILEPATH).openStream()));
//if (br == null) throw new Exception("Error opening file for reading: " + this.PROP_FILEPATH);
ArrayList lines = new ArrayList();
String line = "";
while ((line = br.readLine()) != null) {
if (line.startsWith("#") || (line.length() == 0)) {
lines.add(line);
continue;
}
StringTokenizer st = new StringTokenizer(line, this.PROP_DELIM);
String caseId = st.nextToken();
for (Iterator cIter = cases.iterator(); cIter.hasNext();) {
CBRCase _case = cIter.next();
if (!caseId.equals(_case.getID().toString()))
lines.add(line);
}
}
br.close();
BufferedWriter bw = null;
bw = new BufferedWriter(new FileWriter(FileIO.findFile(this.PROP_FILEPATH).getFile(), false));
//if (bw == null) throw new Exception("Error opening file for writing: "+ this.PROP_FILEPATH);
for (ListIterator lIter = lines.listIterator(); lIter.hasNext();) {
line = lIter.next();
bw.write(line);
bw.newLine();
}
bw.close();
} catch (Exception e) {
LogManager.getLogger(this.getClass()).error(
"Error deleting cases " + e.getMessage());
}
}
/**
* Retrieves all cases from the text file. It maps data types using the
* PlainTextTypeConverter class.
*
* @return Retrieved cases.
*/
public List retrieveAllCases() {
LinkedList cases = new LinkedList();
try {
BufferedReader br = null;
br = new BufferedReader( new InputStreamReader(FileIO.openFile(this.PROP_FILEPATH)));
//if (br == null) throw new Exception("Error opening file: " + this.PROP_FILEPATH);
String line = "";
while ((line = br.readLine()) != null) {
if (line.startsWith("#") || (line.length() == 0))
continue;
StringTokenizer st = new StringTokenizer(line, this.PROP_DELIM);
CBRCase _case = new CBRCase();
CaseComponent description = (CaseComponent)this.descriptionClass.newInstance();
fillComponent(description, st, this.descriptionMaps, true);
_case.setDescription(description);
if(this.solutionClass != null)
{
CaseComponent solution = (CaseComponent)this.solutionClass.newInstance();
fillComponent(solution, st, this.solutionMaps, false);
_case.setSolution(solution);
}
if(this.justOfSolutionClass != null)
{
CaseComponent justificationOfSolution = (CaseComponent)this.justOfSolutionClass.newInstance();
fillComponent(justificationOfSolution, st, this.justOfSolutionMaps, false);
_case.setJustificationOfSolution(justificationOfSolution);
}
if(this.resultClass != null)
{
CaseComponent result = (CaseComponent)this.resultClass.newInstance();
fillComponent(result, st, this.resultMaps, false);
_case.setResult(result);
}
cases.add(_case);
}
br.close();
} catch (Exception e) {
LogManager.getLogger(this.getClass()).error(
"Error retrieving cases " + e.getMessage());
}
return cases;
}
private void fillComponent(CaseComponent component, StringTokenizer st, List maps, boolean includeId)
{
try {
Class> type;
Object value;
if(includeId)
{
Attribute idAttribute = component.getIdAttribute();
type = idAttribute.getType();
value = PlainTextTypeConverter.convert(st.nextToken(), type);
idAttribute.setValue(component, value);
}
for(Attribute at : maps)
{
type = at.getType();
value = PlainTextTypeConverter.convert(st.nextToken(), type);
at.setValue(component, value);
}
} catch (Exception e) {
LogManager.getLogger(this.getClass()).error(
"Error creating case: " + e.getMessage());
}
}
public List retrieveSomeCases(CaseBaseFilter filter) {
return null;
}
}