001/*
002 * Copyright (c) 2015-2020, Oracle and/or its affiliates. All rights reserved.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.tribuo;
018
019import java.io.Serializable;
020import java.util.Comparator;
021import java.util.logging.Level;
022import java.util.logging.Logger;
023
024/**
025 * A class for features. Features are an immutable tuple of name and a double value.
026 * <p>
027 * Features can be manufactured by the {@link Example} and are not expected
028 * to be long lived objects. They may be deconstructed when stored in an Example.
029 * One day they should become value/inline types.
030 */
031public class Feature implements Serializable, Cloneable, Comparable<Feature> {
032    private static final long serialVersionUID = 1L;
033
034    private static final Logger logger = Logger.getLogger(Feature.class.getName());
035
036    /**
037     * The feature name.
038     */
039    protected final String name;
040
041    /**
042     * The feature value.
043     */
044    protected final double value;
045
046    /**
047     * Creates an immutable feature.
048     * @param name The feature name.
049     * @param value The feature value.
050     */
051    public Feature(String name, double value) {
052        this.name = name;
053        this.value = value;
054    }
055
056    /**
057     * Returns the feature name.
058     * @return The feature name
059     */
060    public String getName() {
061        return name;
062    }
063
064    /**
065     * Returns the feature value.
066     * @return The feature value.
067     */
068    public double getValue() {
069        return value;
070    }
071
072    @Override
073    public String toString() {
074        return String.format("(%s, %f)", name, value);
075    }
076
077    /**
078     * Returns the feature name formatted as a table cell.
079     * @return The feature name.
080     */
081    public String toHTML() {
082        String cleanName = getName().replace("&", "&amp;")
083                .replace("<", "&lt;")
084                .replace(">", "&gt;");
085
086        return String.format("<td style=\"text-align:left\">%s</td>", cleanName);
087    }
088
089    @Override
090    public boolean equals(Object o) {
091        if (this == o) return true;
092        if (o == null || getClass() != o.getClass()) return false;
093
094        Feature feature = (Feature) o;
095
096        if (Double.compare(feature.value, value) != 0) return false;
097        return name != null ? name.equals(feature.name) : feature.name == null;
098    }
099
100    @Override
101    public int hashCode() {
102        int result;
103        long temp;
104        result = name != null ? name.hashCode() : 0;
105        temp = Double.doubleToLongBits(value);
106        result = 31 * result + (int) (temp ^ (temp >>> 32));
107        return result;
108    }
109
110    @Override
111    public Feature clone() {
112        try {
113            return (Feature) super.clone();
114        } catch (CloneNotSupportedException e) {
115            logger.log(Level.SEVERE, "Clone failed, returning copy");
116            return new Feature(name,value);
117        }
118    }
119
120    /**
121     * A comparator using the lexicographic ordering of feature names.
122     * @return A lexicographic comparator.
123     */
124    public static Comparator<Feature> featureNameComparator() {
125        return Comparator.comparing(a -> a.name);
126    }
127
128    @Override
129    public int compareTo(Feature o) {
130        return name.compareTo(o.name);
131    }
132}