/*******************************************************************************
* Copyright 2006 - 2012 Vienna University of Technology,
* Department of Software Technology and Interactive Systems, IFS
*
* 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.
*
* This work originates from the Planets project, co-funded by the European Union under the Sixth Framework Programme.
******************************************************************************/
package eu.scape_project.planning.model.scales;
import java.io.Serializable;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.Lob;
import javax.persistence.OneToOne;
import javax.persistence.Transient;
import eu.scape_project.planning.model.ChangeLog;
import eu.scape_project.planning.model.IChangesHandler;
import eu.scape_project.planning.model.ITouchable;
import eu.scape_project.planning.model.tree.Leaf;
import eu.scape_project.planning.model.tree.ObjectiveTree;
import eu.scape_project.planning.model.values.BooleanValue;
import eu.scape_project.planning.model.values.Value;
import eu.scape_project.planning.validation.ValidationError;
/**
* Base class for all scales that are used for measuring the degree of
* fulfillment of requirements, i.e. {@link Leaf} nodes in the
* {@link ObjectiveTree}. These scales have units, restrictions that define
* possible values, and they are capable of constructing instances of
* {@link Value} objects that correspond to their scale. I.e., a
* {@link BooleanScale} will be able to create a {@link BooleanValue}.
* Furthermore, they are also responsible of checking these Values for
* correctness ({@link #isEvaluated(Value)}
*
* @author Christoph Becker
* @author Kevin Stadler
*/
@Entity
@Inheritance
@DiscriminatorColumn(name = "type")
public abstract class Scale implements Serializable, ITouchable, Cloneable {
/**
* We have to set the max and min limits here. Reason: Derby's limits are
* different from the java.lang.Double limits. An exception is thrown when
* any double value is calculated or entered that is outside of these value
* ranges. Arithmetic operations do not round their resulting values to
* zero. If the values are too small, you will receive an exception.
*/
public static final double MAX_VALUE = 1.79769E+308;
/**
* see comment to {@link #MAX_VALUE}
*/
public static final double MIN_VALUE = 2.225E-307;
/**
* Defines the separator to be used in string-encoding of possible
* restriction settings, such as Yes/Acceptable/No or 0/5
*/
public static final String SEPARATOR = "/";
@Id
@GeneratedValue
private int id;
/**
* The measurement unit of the Scale. E.g. "number of tools" supporting a
* file format, "Euro" for the costs of a preservation action, etc.
*/
@Lob
private String unit = new String();
@OneToOne(cascade = CascadeType.ALL)
private ChangeLog changeLog = new ChangeLog();
/**
* This field is needed only for the hibernate annotation transient -
* because annotations need to be consistently on either fields, getters, or
* setters.
*/
@Transient
protected String displayName = "Undefined scale";
/**
* Returns the human readable name of this scale
*
* Needs to be implemented in subclasses.
*
* @return
*/
public abstract String getDisplayName();
/**
* Checks whether the provided {@link Value} object has been evaluated
* properly, i.e. has an associated value that is valid and has been changed
* at some time by the user (because default values might be valid, too).
*
* Needs to be implemented in subclasses.
*
* @param v
* the {@link Value} that shall be checked
* @return true if the Value has been changed by the user.
* @see Value#isChanged()
* @see Value#isEvaluated()
*/
public abstract boolean isEvaluated(Value v);
/**
* This property is only needed for checking in the faces pages if we need
* to display the fields for setting restrictions
*/
@Transient
protected boolean restricted = false;
@Transient
protected List<String> list;
public abstract boolean isRestricted();
/**
* Must be overridden by subclasses to return the type of the scale -
* ordinal or not ordinal, that's the question.
*
* @return {@link ScaleType}
*/
public abstract ScaleType getType();
/**
* Returns true if the basic properties of this value-object are correctly
* specified, e.g. a restricted value has a restriction that is correctly
* formatted etc. If this method returns false, it has to add a
* corresponding error-message to the given list.
*
* @param leafName
* name of the leaf this scale belongs to, need to be provided to
* be able to create sensible and traceable error-messages
* @param errors
* If errors occur, error messages are added to this List.
*/
public abstract boolean isCorrectlySpecified(String leafName, List<ValidationError> errors);
/**
* Creates a {@link Value} object corresponding to this scale.
*
* @return subclass of {@link Value} corresponding to myself.
*/
public abstract Value createValue();
/**
* @see ChangeLog#touch()
*/
public void touch(String username) {
this.changeLog.touch(username);
}
public void touch() {
this.changeLog.touch();
}
public boolean isChanged() {
return changeLog.isAltered();
}
public boolean isDirty() {
return changeLog.isDirty();
}
public ChangeLog getChangeLog() {
return changeLog;
}
public void setChangeLog(ChangeLog value) {
changeLog = value;
}
/**
* @see ITouchable#handleChanges(IChangesHandler)
*/
public void handleChanges(IChangesHandler h) {
h.visit(this);
}
public boolean isInteger() {
return false;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUnit() {
return unit;
}
public void setUnit(String unit) {
this.unit = unit;
}
@Override
public Scale clone() {
try {
Scale clone = (Scale) super.clone();
clone.id = 0;
// created-timestamp is automatically set to now
clone.setChangeLog(new ChangeLog(this.getChangeLog().getChangedBy()));
return clone;
} catch (CloneNotSupportedException e) {
// never thrown
return null;
}
}
public String toString() {
return getClass().getName();
}
public List<String> getList() {
return list;
}
}