/**
* Copyright (C) 2001-2017 by RapidMiner and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapidminer.com
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU Affero General Public License as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
*
* 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
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License along with this program.
* If not, see http://www.gnu.org/licenses/.
*/
package com.rapidminer.example.table;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import com.rapidminer.example.Attribute;
import com.rapidminer.example.AttributeDescription;
import com.rapidminer.example.AttributeTransformation;
import com.rapidminer.example.Attributes;
import com.rapidminer.example.ExampleSet;
import com.rapidminer.example.Statistics;
import com.rapidminer.operator.Annotations;
import com.rapidminer.tools.Ontology;
/**
* This is a possible abstract superclass for all attribute implementations. Most methods of
* {@link Attribute} are already implemented here.
*
* @author Ingo Mierswa
*/
public abstract class AbstractAttribute implements Attribute {
private static final long serialVersionUID = -9167755945651618227L;
private transient List<Attributes> owners = new LinkedList<Attributes>();
/** The basic information about the attribute. Will only be shallowly cloned. */
private AttributeDescription attributeDescription;
private final List<AttributeTransformation> transformations = new ArrayList<AttributeTransformation>();
/** Contains all attribute statistics calculation algorithms. */
private List<Statistics> statistics = new LinkedList<Statistics>();
/** The current attribute construction description object. */
private String constructionDescription = null;
private Annotations annotations = new Annotations();
// --------------------------------------------------------------------------------
/**
* Creates a simple attribute which is not part of a series and does not provide a unit string.
* This constructor should only be used for attributes which were not generated with help of a
* generator, i.e. this attribute has no function arguments. Only the last transformation is
* cloned, the other transformations are cloned by reference.
*/
protected AbstractAttribute(AbstractAttribute attribute) {
this.attributeDescription = attribute.attributeDescription;
// copy statistics
this.statistics = new LinkedList<Statistics>();
for (Statistics statistics : attribute.statistics) {
this.statistics.add((Statistics) statistics.clone());
}
// copy transformations if necessary (only the transformation on top of the view stack!)
int counter = 0;
for (AttributeTransformation transformation : attribute.transformations) {
if (counter < attribute.transformations.size() - 1) {
addTransformation(transformation);
} else {
addTransformation((AttributeTransformation) transformation.clone());
}
counter++;
}
// copy construction description
this.constructionDescription = attribute.constructionDescription;
// copy annotations
annotations.putAll(attribute.getAnnotations());
}
/**
* Creates a simple attribute which is not part of a series and does not provide a unit string.
* This constructor should only be used for attributes which were not generated with help of a
* generator, i.e. this attribute has no function arguments.
*/
protected AbstractAttribute(String name, int valueType) {
this.attributeDescription = new AttributeDescription(this, name, valueType, Ontology.SINGLE_VALUE, 0.0d,
UNDEFINED_ATTRIBUTE_INDEX);
this.constructionDescription = name;
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
if (owners == null) {
owners = new LinkedList<Attributes>();
}
if (annotations == null) {
annotations = new Annotations();
}
}
@Override
public void addOwner(Attributes attributes) {
this.owners.add(attributes);
}
@Override
public void removeOwner(Attributes attributes) {
this.owners.remove(attributes);
}
/** Clones this attribute. */
@Override
public abstract Object clone();
/**
* Returns true if the given attribute has the same name and the same table index.
*/
@Override
public boolean equals(Object o) {
if (!(o instanceof AbstractAttribute)) {
return false;
}
AbstractAttribute a = (AbstractAttribute) o;
return this.attributeDescription.equals(a.attributeDescription);
}
@Override
public int hashCode() {
return attributeDescription.hashCode();
}
@Override
public void addTransformation(AttributeTransformation transformation) {
this.transformations.add(transformation);
}
@Override
public void clearTransformations() {
this.transformations.clear();
}
@Override
public AttributeTransformation getLastTransformation() {
int size = this.transformations.size();
if (size > 0) {
return this.transformations.get(size - 1);
} else {
return null;
}
}
@Override
public double getValue(DataRow row) {
double result = row.get(getTableIndex(), getDefault());
if (!transformations.isEmpty()) {
for (AttributeTransformation transformation : transformations) {
result = transformation.transform(this, result);
}
}
return result;
}
@Override
public void setValue(DataRow row, double value) {
double newValue = value;
for (AttributeTransformation transformation : transformations) {
if (transformation.isReversable()) {
newValue = transformation.inverseTransform(this, newValue);
} else {
throw new RuntimeException(
"Cannot set value for attribute using irreversible transformations. This process will probably work if you deactivate create_view in preprocessing operators.");
}
}
row.set(getTableIndex(), newValue, getDefault());
}
/** Returns the name of the attribute. */
@Override
public String getName() {
return this.attributeDescription.getName();
}
/** Sets the name of the attribute. */
@Override
public void setName(String v) {
if (v.equals(this.attributeDescription.getName())) {
return;
}
for (Attributes attributes : owners) {
attributes.rename(this, v);
}
this.attributeDescription = (AttributeDescription) this.attributeDescription.clone();
this.attributeDescription.setName(v);
}
/** Returns the index in the example table. */
@Override
public int getTableIndex() {
return this.attributeDescription.getTableIndex();
}
/** Sets the index in the example table. */
@Override
public void setTableIndex(int i) {
this.attributeDescription = (AttributeDescription) this.attributeDescription.clone();
this.attributeDescription.setTableIndex(i);
}
// --- meta data of data ---
/**
* Returns the block type of this attribute.
*
* @see com.rapidminer.tools.Ontology#ATTRIBUTE_BLOCK_TYPE
*/
@Override
public int getBlockType() {
return this.attributeDescription.getBlockType();
}
/**
* Sets the block type of this attribute.
*
* @see com.rapidminer.tools.Ontology#ATTRIBUTE_BLOCK_TYPE
*/
@Override
public void setBlockType(int b) {
this.attributeDescription = (AttributeDescription) this.attributeDescription.clone();
this.attributeDescription.setBlockType(b);
}
/**
* Returns the value type of this attribute.
*
* @see com.rapidminer.tools.Ontology#ATTRIBUTE_VALUE_TYPE
*/
@Override
public int getValueType() {
return this.attributeDescription.getValueType();
}
/** Returns the attribute statistics. */
@Override
public Iterator<Statistics> getAllStatistics() {
return this.statistics.iterator();
}
@Override
public void registerStatistics(Statistics statistics) {
this.statistics.add(statistics);
}
/**
* Returns the attribute statistics.
*
* @deprecated Please use the method {@link ExampleSet#getStatistics(Attribute, String)}
* instead.
*/
@Override
@Deprecated
public double getStatistics(String name) {
return getStatistics(name, null);
}
/**
* Returns the attribute statistics.
*
* @deprecated Please use the method {@link ExampleSet#getStatistics(Attribute, String)}
* instead.
*/
@Override
@Deprecated
public double getStatistics(String name, String parameter) {
for (Statistics statistics : this.statistics) {
if (statistics.handleStatistics(name)) {
return statistics.getStatistics(this, name, parameter);
}
}
throw new RuntimeException("No statistics object was available for attribute statistics '" + name + "'!");
}
/** Returns the construction description. */
@Override
public String getConstruction() {
return this.constructionDescription;
}
/** Returns the construction description. */
@Override
public void setConstruction(String description) {
this.constructionDescription = description;
}
// ================================================================================
// default value
// ================================================================================
@Override
public void setDefault(double value) {
this.attributeDescription = (AttributeDescription) this.attributeDescription.clone();
this.attributeDescription.setDefault(value);
}
@Override
public double getDefault() {
return this.attributeDescription.getDefault();
}
// ================================================================================
// string and result methods
// ================================================================================
/** Returns a human readable string that describes this attribute. */
@Override
public String toString() {
StringBuffer result = new StringBuffer();
result.append("#");
result.append(this.attributeDescription.getTableIndex());
result.append(": ");
result.append(this.attributeDescription.getName());
result.append(" (");
result.append(Ontology.ATTRIBUTE_VALUE_TYPE.mapIndex(this.attributeDescription.getValueType()));
result.append("/");
result.append(Ontology.ATTRIBUTE_BLOCK_TYPE.mapIndex(this.attributeDescription.getBlockType()));
result.append(")");
return result.toString();
}
@Override
public Annotations getAnnotations() {
return annotations;
}
}