blob: c69c61e69de0dcc4667b408258cdcaa803ac681c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016 University of York
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* Contributors:
* Horacio Hoyos - Initial API and implementation
*******************************************************************************/
package org.eclipse.epsilon.emc.csvpro;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.eclipse.epsilon.common.util.FileUtil;
import org.eclipse.epsilon.common.util.StringProperties;
import org.eclipse.epsilon.eol.exceptions.EolRuntimeException;
import org.eclipse.epsilon.eol.exceptions.models.EolEnumerationValueNotFoundException;
import org.eclipse.epsilon.eol.exceptions.models.EolModelElementTypeNotFoundException;
import org.eclipse.epsilon.eol.exceptions.models.EolModelLoadingException;
import org.eclipse.epsilon.eol.exceptions.models.EolNotInstantiableModelElementTypeException;
import org.eclipse.epsilon.eol.execute.introspection.AbstractPropertyGetter;
import org.eclipse.epsilon.eol.execute.introspection.AbstractPropertySetter;
import org.eclipse.epsilon.eol.execute.introspection.IPropertyGetter;
import org.eclipse.epsilon.eol.execute.introspection.IPropertySetter;
import org.eclipse.epsilon.eol.models.CachedModel;
import org.eclipse.epsilon.eol.models.IRelativePathResolver;
// TODO: Auto-generated Javadoc
/**
* The Class CsvProModel.
*/
public class CsvProModel extends CachedModel<Integer> {
/**
* The Class CsvPropertyGetter.
*/
private class CsvPropertyGetter extends AbstractPropertyGetter {
/* (non-Javadoc)
* @see org.eclipse.epsilon.eol.execute.introspection.IPropertyGetter#invoke(java.lang.Object, java.lang.String)
*/
@Override
public Object invoke(Object object, String property) throws EolRuntimeException {
assert object instanceof Integer;
return data.get(property).get((int) object);
}
}
/**
* The Class CsvPropertySetter.
*/
private class CsvPropertySetter extends AbstractPropertySetter {
/* (non-Javadoc)
* @see org.eclipse.epsilon.eol.execute.introspection.IPropertySetter#invoke(java.lang.Object)
*/
@Override
public void invoke(Object value) throws EolRuntimeException {
data.get(property).set((int) object, value.toString());
}
}
/** The Constant PROPERTY_FILE. */
public static final String PROPERTY_FILE = "file";
/** The Constant PROPERTY_FIELD_SEPARATOR. */
public static final String PROPERTY_FIELD_SEPARATOR = "separator";
/** The Constant PROPERTY_HAS_KNOWN_HEADERS. */
public static final String PROPERTY_HAS_KNOWN_HEADERS = "useheaders";
/** The Constant PROPERTY_HAS_VARARGS_HEADERS. */
public static final String PROPERTY_HAS_VARARGS_HEADERS = "vararg";
/** The Constant PROPERTY_USE_TYPE_COLUMN. */
public static final String PROPERTY_USE_TYPE_COLUMN = "usetypecolumn";
/** The Constant PROPERTY_TYPE_COLUMN. */
public static final String PROPERTY_TYPE_COLUMN = "typecolumn";
/** The Constant PROPERTY_USE_INDEX_COLUMN. */
public static final String PROPERTY_USE_INDEX_COLUMN = "useindexcolumn";
/** The Constant PROPERTY_INDEX_COLUMN. */
public static final String PROPERTY_INDEX_COLUMN = "indexcolumn";
/** The store the CSV data in columns mapped by column name. */
private Map<String, List<String>> data;
/** The rows. */
private TreeMap<String, Integer> rows; // Keep track of rows index and allow add/delete
/** The typed elements. */
private Map<String, CsvProCollection> typedElements;
/** The pg. */
private CsvPropertyGetter pg;
/** The ps. */
private CsvPropertySetter ps;
/** The file. */
private String file;
/** The field separator. */
private String fieldSeparator;
/** The known headers. */
private boolean knownHeaders;
/** The varargs headers. */
private boolean varargsHeaders;
/** The type colum. */
private int typeColum;
/** The type colum name. */
private String typeColumName;
/** The use type colum. */
private boolean useTypeColum;
/** The index colum. */
private int indexColum;
/** The use index colum. */
private boolean useIndexColum;
/** The index colum name. */
private String indexColumName;
/** The all contents cache. */
private CsvProCollection allContentsCache;
/**
* Instantiates a new csv pro model.
*/
public CsvProModel() {
super();
pg = new CsvPropertyGetter();
ps = new CsvPropertySetter();
}
/* (non-Javadoc)
* @see org.eclipse.epsilon.eol.models.CachedModel#allContents()
*/
public Collection<Integer> allContents() {
if (!isCachingEnabled()) {
CsvProCollection ret = getNewCsvProCollection();
ret.setRows(rows);
return ret;
}
if (allContentsCache == null) {
allContentsCache = getNewCsvProCollection();
allContentsCache.setRows(rows);
}
return allContentsCache;
}
/* (non-Javadoc)
* @see org.eclipse.epsilon.eol.models.CachedModel#getAllOfType(java.lang.String)
*/
public Collection<Integer> getAllOfType(String type) throws EolModelElementTypeNotFoundException {
if (!isCachingEnabled()) {
return getAllOfTypeFromModel(type);
}
else {
return super.getAllOfType(type);
}
}
/* (non-Javadoc)
* @see org.eclipse.epsilon.eol.models.CachedModel#getAllOfKind(java.lang.String)
*/
public Collection<Integer> getAllOfKind(String kind) throws EolModelElementTypeNotFoundException {
if (!isCachingEnabled()) {
return getAllOfKindFromModel(kind);
}
else {
return super.getAllOfKind(kind);
}
}
/* (non-Javadoc)
* @see org.eclipse.epsilon.eol.models.CachedModel#allContentsFromModel()
*/
@Override
protected Collection<Integer> allContentsFromModel() {
CsvProCollection values = getNewCsvProCollection();
values.setRows(rows);
return values;
}
/**
* Gets the new csv pro collection.
*
* @return the new csv pro collection
*/
private CsvProCollection getNewCsvProCollection() {
if (useIndexColum) {
return new CsvProCollection(indexColumName);
}
else {
return new CsvProCollection();
}
}
/**
* When creating an instance several consideration have to be taken into consideration:
* - If no typing, then a new entry in the data map must be craeted to add "blank" field values for all attributes
* of the new row
* - If typed, then the attribute used for typing should be set accordingly and the new row shouls also be added
* to the typedEloements map
* - If indexing is used, during instantiation there is no valid value for the id attribute, so it can be added with
* a blank id to the rows tree. However, this will not work if several elements ace created in batch. A
* possibility is to use the row number as an id temporally, but care has to be taken to avoid clashes. The
* PropertySetter must be made aware of indexing so it can fix the key value for an object when the id or do it
* via the setElementId, but then make sure that the id attribute is read only.
*/
@Override
protected Integer createInstanceInModel(String type)
throws EolModelElementTypeNotFoundException, EolNotInstantiableModelElementTypeException {
throw new UnsupportedOperationException("This method is left unimplemented for the users to complete the example.");
}
/**
* The row information must be deleted from all data structures.
*/
@Override
protected boolean deleteElementInModel(Object instance) throws EolRuntimeException {
throw new UnsupportedOperationException("This method is left unimplemented for the users to complete the example.");
}
/* (non-Javadoc)
* @see org.eclipse.epsilon.eol.models.CachedModel#disposeModel()
*/
@Override
protected void disposeModel() {
data.clear();
data = null;
rows.clear();
rows = null;
}
/* (non-Javadoc)
* @see org.eclipse.epsilon.eol.models.CachedModel#getAllOfKindFromModel(java.lang.String)
*/
@Override
protected Collection<Integer> getAllOfKindFromModel(String kind)
throws EolModelElementTypeNotFoundException {
return getAllOfTypeFromModel(kind);
}
/* (non-Javadoc)
* @see org.eclipse.epsilon.eol.models.CachedModel#getAllOfTypeFromModel(java.lang.String)
*/
@Override
protected Collection<Integer> getAllOfTypeFromModel(String type)
throws EolModelElementTypeNotFoundException {
if (!("Row".equals(type) || (useTypeColum && typedElements.containsKey(type)))) {
throw new EolModelElementTypeNotFoundException(this.name, type);
}
if ("Row".equals(type))
return allContents();
else
return typedElements.get(type);
}
/* (non-Javadoc)
* @see org.eclipse.epsilon.eol.models.CachedModel#getAllTypeNamesOf(java.lang.Object)
*/
@Override
protected Collection<String> getAllTypeNamesOf(Object instance) {
if (owns(instance)) {
List<String> result = new ArrayList<>();
result.add("Row");
if (useTypeColum) {
result.add(data.get(typeColumName).get((int) instance));
}
return result;
}
return Collections.emptyList();
}
/* (non-Javadoc)
* @see org.eclipse.epsilon.eol.models.CachedModel#getCacheKeyForType(java.lang.String)
*/
@Override
protected Object getCacheKeyForType(String type) throws EolModelElementTypeNotFoundException {
return type;
}
/* (non-Javadoc)
* @see org.eclipse.epsilon.eol.models.IModel#getElementById(java.lang.String)
*/
@Override
public Object getElementById(String id) {
return rows.get(id);
}
/* (non-Javadoc)
* @see org.eclipse.epsilon.eol.models.IModel#getElementId(java.lang.Object)
*/
@Override
public String getElementId(Object instance) {
assert owns(instance);
return data.get(indexColumName).get((int) instance);
}
/* (non-Javadoc)
* @see org.eclipse.epsilon.eol.models.IModel#getEnumerationValue(java.lang.String, java.lang.String)
*/
@Override
public Object getEnumerationValue(String enumeration, String label) throws EolEnumerationValueNotFoundException {
throw new UnsupportedOperationException("Not Implemented");
}
/* (non-Javadoc)
* @see org.eclipse.epsilon.eol.models.Model#getPropertyGetter()
*/
@Override
public IPropertyGetter getPropertyGetter() {
return pg;
}
/* (non-Javadoc)
* @see org.eclipse.epsilon.eol.models.Model#getPropertySetter()
*/
@Override
public IPropertySetter getPropertySetter() {
return ps;
}
/* (non-Javadoc)
* @see org.eclipse.epsilon.eol.models.IModel#getTypeNameOf(java.lang.Object)
*/
@Override
public String getTypeNameOf(Object instance) {
if (owns(instance)) {
if (useTypeColum) {
return data.get(typeColumName).get((int) instance);
}
return "Row";
}
return null;
}
/* (non-Javadoc)
* @see org.eclipse.epsilon.eol.models.IModel#hasType(java.lang.String)
*/
@Override
public boolean hasType(String type) {
return "Row".equals(type) || (useTypeColum && typedElements.containsKey(type));
}
/* (non-Javadoc)
* @see org.eclipse.epsilon.eol.models.IModel#isInstantiable(java.lang.String)
*/
@Override
public boolean isInstantiable(String type) {
if (!useTypeColum) {
return "Row".equals(type);
}
else {
return typedElements.containsKey(type);
}
}
/* (non-Javadoc)
* @see org.eclipse.epsilon.eol.models.CachedModel#load(org.eclipse.epsilon.common.util.StringProperties, org.eclipse.epsilon.eol.models.IRelativePathResolver)
*/
@Override
public void load(StringProperties properties, IRelativePathResolver resolver) throws EolModelLoadingException {
super.load(properties, resolver);
this.file = resolver.resolve(properties.getProperty(PROPERTY_FILE));
this.fieldSeparator = properties.getProperty(PROPERTY_FIELD_SEPARATOR);
this.knownHeaders = properties.getBooleanProperty(PROPERTY_HAS_KNOWN_HEADERS, true);
this.varargsHeaders = properties.getBooleanProperty(PROPERTY_HAS_VARARGS_HEADERS, false);
this.typeColum = properties.getIntegerProperty(PROPERTY_TYPE_COLUMN, 0);
this.useTypeColum = properties.getBooleanProperty(PROPERTY_USE_TYPE_COLUMN, false);
this.indexColum = properties.getIntegerProperty(PROPERTY_INDEX_COLUMN, 0);
this.useIndexColum = properties.getBooleanProperty(PROPERTY_USE_INDEX_COLUMN, false);
load();
}
/* (non-Javadoc)
* @see org.eclipse.epsilon.eol.models.CachedModel#loadModel()
*/
@Override
protected void loadModel() throws EolModelLoadingException {
LinkedList<String> lines = null;
try {
lines = new LinkedList<>(FileUtil.getFileLineContents(new File(file)));
} catch (Exception ex) {
throw new EolModelLoadingException(ex, this);
} finally {
if(this.knownHeaders) {
if (useTypeColum) {
typedElements = new HashMap<>(lines.size());
}
rows = new TreeMap<>();
List<String> keys = Arrays.asList(lines.get(0).split(this.fieldSeparator));
data = new HashMap<>(keys.size());
if (useTypeColum) {
typeColumName = keys.get(typeColum);
}
if (useIndexColum) {
indexColumName = keys.get(indexColum);
}
for (String k : keys) {
data.put(k, new ArrayList<String>());
}
List<String> values;
for (int i=1; i < lines.size(); i++) {
int index = i-1;
values = Arrays.asList(lines.get(i).split(this.fieldSeparator));
if (keys.size() != values.size()) {
Exception ex = new Exception("Line " + (i+1) + " contains different number of elements than the header");
throw new EolModelLoadingException(ex, this);
}
for (int f=0; f<keys.size(); f++) {
List<String> datavals = data.get(keys.get(f));
if (useTypeColum && (f == typeColum)) {
CsvProCollection typed = typedElements.get(values.get(f));
if (typed == null) {
typed = getNewCsvProCollection();
typed.setRows(new TreeMap<String, Integer>());
typedElements.put(values.get(f), typed);
}
if (useIndexColum) {
typed.put(values.get(indexColum), index);
}
else {
typed.put(String.valueOf(index), index);
}
}
if (useIndexColum && (f == indexColum)) {
rows.put(values.get(f), index);
}
else {
rows.put(String.valueOf(index), index);
}
datavals.add(values.get(f));
}
}
} else {
Exception ex = new Exception("CSV Pro models need headers");
throw new EolModelLoadingException(ex, this);
}
}
}
/* (non-Javadoc)
* @see org.eclipse.epsilon.eol.models.IModel#owns(java.lang.Object)
*/
@Override
public boolean owns(Object instance) {
return rows.containsValue(instance);
}
/* (non-Javadoc)
* @see org.eclipse.epsilon.eol.models.IModel#setElementId(java.lang.Object, java.lang.String)
*/
@Override
public void setElementId(Object instance, String newId) {
// Probably id change is allowed when indexing is selected
throw new UnsupportedOperationException("Row Id can not be changed");
}
/* (non-Javadoc)
* @see org.eclipse.epsilon.eol.models.IModel#store()
*/
@Override
public boolean store() {
throw new UnsupportedOperationException("Not Implemented");
}
/* (non-Javadoc)
* @see org.eclipse.epsilon.eol.models.IModel#store(java.lang.String)
*/
@Override
public boolean store(String location) {
throw new UnsupportedOperationException("Not Implemented");
}
}