/*
* RapidMiner
*
* Copyright (C) 2001-2011 by Rapid-I and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapid-i.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.operator.ports.metadata;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.rapidminer.example.ExampleSet;
import com.rapidminer.operator.ProcessSetupError.Severity;
import com.rapidminer.operator.ports.InputPort;
import com.rapidminer.operator.ports.quickfix.AttributeSelectionQuickFix;
import com.rapidminer.operator.ports.quickfix.QuickFix;
import com.rapidminer.parameter.ParameterHandler;
import com.rapidminer.parameter.UndefinedParameterError;
import com.rapidminer.tools.Ontology;
/**
* This precondition might be used to ensure that a number of attributes is contained in the exampleSet at the given
* port. If the attribute(s) are not contained, only a warning will be given. Since this Precondition does not register
* errors beside the warning, it might be used in addition to the ExampleSetPrecondition.
*
* An implementation of the AttributeNameProvider might be used to provide the names of attribute unknown during
* creation time.
*
* @author Sebastian Land
*
*/
public class AttributeSetPrecondition extends AbstractPrecondition {
public static abstract class AttributeNameProvider {
public abstract String[] getRequiredAttributeNames();
}
private static class ParameterAttributeNameProvider extends AttributeNameProvider {
private final ParameterHandler handler;
private final String[] parameterKeys;
public ParameterAttributeNameProvider(ParameterHandler handler, String... parameterKeys) {
this.handler = handler;
this.parameterKeys = parameterKeys;
}
@Override
public String[] getRequiredAttributeNames() {
ArrayList<String> names = new ArrayList<String>();
for (String key : parameterKeys) {
try {
names.add(handler.getParameterAsString(key));
} catch (UndefinedParameterError e) {
}
}
return names.toArray(new String[names.size()]);
}
public ParameterHandler getHandler() {
return this.handler;
}
public String[] getParameterKeys() {
return this.parameterKeys;
}
}
private static class ParameterListAttributeNameProvider extends AttributeNameProvider {
private final ParameterHandler handler;
private final String parameterKey;
private final int entry;
public ParameterListAttributeNameProvider(ParameterHandler handler, String parameterKey, int entry) {
this.handler = handler;
this.parameterKey = parameterKey;
this.entry = entry;
}
@Override
public String[] getRequiredAttributeNames() {
try {
if (handler.isParameterSet(parameterKey)) {
List<String[]> parameterList = handler.getParameterList(parameterKey);
String[] attributeNames = new String[parameterList.size()];
int i = 0;
for (String[] pair: parameterList) {
attributeNames[i] = pair[entry];
i++;
}
return attributeNames;
}
} catch (UndefinedParameterError e) {
}
return new String[0];
}
public ParameterHandler getHandler() {
return this.handler;
}
public String getParameterKey() {
return this.parameterKey;
}
}
private final String[] requiredAttributes;
private final AttributeNameProvider requiredNameProvider;
private final int requiredAttributesType;
public AttributeSetPrecondition(InputPort inputPort, String... requiredAttributeNames) {
this(inputPort, null, requiredAttributeNames);
}
public AttributeSetPrecondition(InputPort inputPort, AttributeNameProvider attributeNameProvider, String... requiredAttributeNames) {
this(inputPort, attributeNameProvider, Ontology.VALUE_TYPE, requiredAttributeNames);
}
public AttributeSetPrecondition(InputPort inputPort, AttributeNameProvider attributeNameProvider, int typeOfRequiredAttributes, String... requiredAttributeNames) {
super(inputPort);
this.requiredAttributes = requiredAttributeNames;
this.requiredNameProvider = attributeNameProvider;
this.requiredAttributesType = typeOfRequiredAttributes;
}
@Override
public void assumeSatisfied() {
getInputPort().receiveMD(new ExampleSetMetaData());
}
@Override
public void check(MetaData metaData) {
if (metaData != null) {
if (metaData instanceof ExampleSetMetaData) {
ExampleSetMetaData emd = (ExampleSetMetaData) metaData;
// checking attribute names
checkAttributeNames(requiredAttributes, emd);
// checking provider name
if (requiredNameProvider != null) {
checkAttributeNames(requiredNameProvider.getRequiredAttributeNames(), emd);
}
makeAdditionalChecks(emd);
}
}
}
private void checkAttributeNames(String[] requiredNames, ExampleSetMetaData emd) {
for (String attributeName : requiredNames) {
if (!attributeName.contains("%{")) {
if (attributeName != null && attributeName.length() > 0) {
MetaDataInfo attInfo = emd.containsAttributeName(attributeName);
if (attInfo == MetaDataInfo.NO) {
QuickFix fix = null;
try {
fix = getQuickFix(emd);
} catch (UndefinedParameterError e) {
}
Severity sev = Severity.WARNING;
if (emd.getAttributeSetRelation() == SetRelation.EQUAL)
sev = Severity.ERROR;
if (fix != null)
createError(sev, Collections.singletonList(fix), "missing_attribute", attributeName);
else
createError(sev, "missing_attribute", attributeName);
} else if (attInfo == MetaDataInfo.YES) {
AttributeMetaData amd = emd.getAttributeByName(attributeName);
if (!Ontology.ATTRIBUTE_VALUE_TYPE.isA(amd.getValueType(), requiredAttributesType)) {
QuickFix fix = null;
try {
fix = getQuickFix(emd);
} catch (UndefinedParameterError e) {
}
if (fix != null)
createError(Severity.ERROR, Collections.singletonList(fix), "attribute_has_wrong_type", amd.getName(), Ontology.VALUE_TYPE_NAMES[requiredAttributesType]);
else
createError(Severity.ERROR, "attribute_has_wrong_type", amd.getName(), Ontology.VALUE_TYPE_NAMES[requiredAttributesType]);
}
}
}
}
}
}
protected final String[] getRequiredNames() {
int length = requiredAttributes.length;
String[] providedNames = requiredNameProvider.getRequiredAttributeNames();
if (requiredNameProvider != null)
length += providedNames.length;
String[] result = new String[length];
for (int i = 0; i < requiredAttributes.length; i++) {
result[i] = requiredAttributes[i];
}
for (int i = 0; i < providedNames.length; i++) {
result[i + requiredAttributes.length] = providedNames[i];
}
return result;
}
/** Can be implemented by subclasses in order to specify quickfixes. */
public QuickFix getQuickFix(ExampleSetMetaData emd) throws UndefinedParameterError {
if (requiredNameProvider != null) {
if (requiredNameProvider instanceof ParameterAttributeNameProvider) {
ParameterAttributeNameProvider provider = (ParameterAttributeNameProvider) requiredNameProvider;
if (provider.getParameterKeys().length == 1 && provider.getRequiredAttributeNames().length == 1) {
return new AttributeSelectionQuickFix(emd, provider.getParameterKeys()[0], provider.getHandler(), provider.getRequiredAttributeNames()[0], requiredAttributesType);
}
}
}
return null;
}
/** Can be implemented by subclasses. */
public void makeAdditionalChecks(ExampleSetMetaData emd) {
}
@Override
public String getDescription() {
return "<em>expects:</em> ExampleSet";
}
@Override
public boolean isCompatible(MetaData input, CompatibilityLevel level) {
return ExampleSet.class.isAssignableFrom(input.getObjectClass());
}
@Override
public MetaData getExpectedMetaData() {
return new ExampleSetMetaData();
}
public static AttributeNameProvider getAttributesByParameter(ParameterHandler handler, String... parameterKeys) {
return new ParameterAttributeNameProvider(handler, parameterKeys);
}
/**
* Returns an AttributeNameProvider that can extract one column of a ParameterTypeList for use them as attribute names.
*/
public static AttributeNameProvider getAttributesByParameterListEntry(ParameterHandler handler, String parameterListKey, int entry) {
return new ParameterListAttributeNameProvider(handler, parameterListKey, entry);
}
}