package water.api.schemas3;
import java.lang.reflect.Field;
import water.AutoBuffer;
import water.H2O;
import water.Iced;
import water.IcedWrapper;
import water.api.API;
import water.api.SchemaMetadata.FieldMetadata;
import water.util.PojoUtils;
// TODO: move into hex.schemas!
/**
* An instance of a ModelParameters schema contains the metadata for a single Model build parameter (e.g., K for KMeans).
* TODO: add a superclass.
* TODO: refactor this into with FieldMetadataBase.
*/
public class ModelParameterSchemaV3 extends SchemaV3<Iced, ModelParameterSchemaV3> {
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAREFUL: This class has its own JSON serializer. If you add a field here you probably also want to add it to the serializer!
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@API(help="name in the JSON, e.g. \"lambda\"", direction=API.Direction.OUTPUT)
public String name;
@API(help="[DEPRECATED] same as name.", direction=API.Direction.OUTPUT)
public String label;
@API(help="help for the UI, e.g. \"regularization multiplier, typically used for foo bar baz etc.\"", direction=API.Direction.OUTPUT)
public String help;
@API(help="the field is required", direction=API.Direction.OUTPUT)
public boolean required;
@API(help="Java type, e.g. \"double\"", direction=API.Direction.OUTPUT)
public String type;
@API(help="default value, e.g. 1", direction=API.Direction.OUTPUT)
public Iced default_value;
@API(help="actual value as set by the user and / or modified by the ModelBuilder, e.g., 10", direction=API.Direction.OUTPUT)
public Iced actual_value;
@API(help="the importance of the parameter, used by the UI, e.g. \"critical\", \"extended\" or \"expert\"", direction=API.Direction.OUTPUT)
public String level;
@API(help="list of valid values for use by the front-end", direction=API.Direction.OUTPUT)
public String[] values;
@API(help="For Vec-type fields this is the set of other Vec-type fields which must contain mutually exclusive values; for example, for a SupervisedModel the response_column must be mutually exclusive with the weights_column")
public String[] is_member_of_frames;
@API(help="For Vec-type fields this is the set of Frame-type fields which must contain the named column; for example, for a SupervisedModel the response_column must be in both the training_frame and (if it's set) the validation_frame")
public String[] is_mutually_exclusive_with;
@API(help="Parameter can be used in grid call", direction=API.Direction.OUTPUT)
public boolean gridable;
public ModelParameterSchemaV3() {
}
/** TODO: refactor using SchemaMetadata. */
public ModelParameterSchemaV3(ModelParametersSchemaV3 schema, ModelParametersSchemaV3 default_schema, Field f) {
f.setAccessible(true);
try {
this.name = f.getName();
API annotation = f.getAnnotation(API.class);
boolean is_array = f.getType().isArray();
Object o;
o = f.get(default_schema);
this.default_value = FieldMetadata.consValue(o);
o = f.get(schema);
this.actual_value = FieldMetadata.consValue(o);
boolean is_enum = Enum.class.isAssignableFrom(f.getType());
this.type = FieldMetadata.consType(schema, f.getType(), f.getName(), annotation);
if (null != annotation) {
String l = annotation.label();
this.label = this.name;
this.help = annotation.help();
this.required = annotation.required();
this.level = annotation.level().toString();
this.values = annotation.values();
// If the field is an enum then the values annotation field had better be set. . .
if (is_enum && (null == this.values || 0 == this.values.length)) {
throw H2O.fail("Didn't find values annotation for enum field: " + this.name);
}
// NOTE: we just set the raw value here. We compute the transitive closure
// before serializing to JSON. We have to do this automagically since we
// need to combine the values from multiple fields in multiple levels of the
// inheritance hierarchy.
this.is_member_of_frames = annotation.is_member_of_frames();
this.is_mutually_exclusive_with = annotation.is_mutually_exclusive_with(); // NOTE: later we walk all the fields in the Schema and form the transitive closure of these lists.
this.gridable = annotation.gridable();
}
}
catch (Exception e) {
throw H2O.fail("Caught exception accessing field: " + f + " for schema object: " + this + ": " + e.toString());
}
}
public ModelParameterSchemaV3 fillFromImpl(Iced iced) {
PojoUtils.copyProperties(this, iced, PojoUtils.FieldNaming.ORIGIN_HAS_UNDERSCORES);
return this;
}
public Iced createImpl() {
// should never get called
throw H2O.fail("createImpl should never get called in ModelParameterSchemaV2!");
}
/**
* ModelParameterSchema has its own serializer so that default_value and actual_value
* get serialized as their native types. Autobuffer assumes all classes that have their
* own serializers should be serialized as JSON objects, and wraps them in {}, so this
* can't just be handled by a customer serializer in IcedWrapper.
*
* @param ab
* @return
*/
public final AutoBuffer writeJSON_impl(AutoBuffer ab) {
ab.putJSONStr("name", name); ab.put1(',');
ab.putJSONStr("label", name); ab.put1(',');
ab.putJSONStr("help", help); ab.put1(',');
ab.putJSONStrUnquoted("required", required ? "true" : "false"); ab.put1(',');
ab.putJSONStr("type", type); ab.put1(',');
if (default_value instanceof IcedWrapper) {
ab.putJSONStr("default_value").put1(':');
((IcedWrapper) default_value).writeUnwrappedJSON(ab); ab.put1(',');
} else {
ab.putJSONStr("default_value").put1(':').putJSON(default_value); ab.put1(',');
}
if (actual_value instanceof IcedWrapper) {
ab.putJSONStr("actual_value").put1(':');
((IcedWrapper) actual_value).writeUnwrappedJSON(ab); ab.put1(',');
} else {
ab.putJSONStr("actual_value").put1(':').putJSON(actual_value); ab.put1(',');
}
ab.putJSONStr("level", level); ab.put1(',');
ab.putJSONAStr("values", values); ab.put1(',');
ab.putJSONAStr("is_member_of_frames", is_member_of_frames); ab.put1(',');
ab.putJSONAStr("is_mutually_exclusive_with", is_mutually_exclusive_with); ab.put1(',');
ab.putJSONStrUnquoted("gridable", gridable ? "true" : "false");
return ab;
}
}