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.hash;
018
019import com.oracle.labs.mlrg.olcut.config.Config;
020import com.oracle.labs.mlrg.olcut.provenance.ConfiguredObjectProvenance;
021import com.oracle.labs.mlrg.olcut.provenance.ObjectProvenance;
022import com.oracle.labs.mlrg.olcut.provenance.Provenance;
023import com.oracle.labs.mlrg.olcut.provenance.primitives.IntProvenance;
024import com.oracle.labs.mlrg.olcut.provenance.primitives.StringProvenance;
025
026import java.io.IOException;
027import java.io.ObjectInputStream;
028import java.util.HashMap;
029import java.util.Map;
030import java.util.Objects;
031
032/**
033 * Hashes names using String.hashCode(), then reduces the dimension.
034 */
035public final class ModHashCodeHasher extends Hasher {
036    private static final long serialVersionUID = 2L;
037
038    static final String DIMENSION = "dimension";
039
040    @Config(mandatory = true,description="Salt used in the hash.")
041    private transient String salt = null;
042
043    @Config(mandatory = true,description="Range of the hashing function.")
044    private int dimension = 100;
045
046    private ModHashCodeHasherProvenance provenance;
047
048    /**
049     * for olcut.
050     */
051    private ModHashCodeHasher() { }
052
053    public ModHashCodeHasher(String salt) {
054        this(100,salt);
055    }
056
057    public ModHashCodeHasher(int dimension, String salt) {
058        this.dimension = dimension;
059        this.salt = salt;
060        postConfig();
061    }
062
063    /**
064     * Used by the OLCUT configuration system, and should not be called by external code.
065     */
066    @Override
067    public void postConfig() {
068        this.provenance = new ModHashCodeHasherProvenance(dimension);
069    }
070
071    @Override
072    public String hash(String name) {
073        if (salt == null) {
074            throw new IllegalStateException("Salt not set");
075        }
076        String salted = salt + name;
077        return ""+(salted.hashCode() % dimension);
078    }
079
080    @Override
081    public void setSalt(String salt) {
082        if (Hasher.validateSalt(salt)) {
083            this.salt = salt;
084        } else {
085            throw new IllegalArgumentException("Salt: '" + salt + ", does not meet the requirements for a salt.");
086        }
087    }
088
089    @Override
090    public ConfiguredObjectProvenance getProvenance() {
091        return provenance;
092    }
093
094    @Override
095    public String toString() {
096        return "ModHashCodeHasher(dimension=" + dimension + ")";
097    }
098
099    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
100        in.defaultReadObject();
101        salt = null;
102    }
103
104    /**
105     * Provenance for the {@link ModHashCodeHasher}.
106     */
107    public final static class ModHashCodeHasherProvenance implements ConfiguredObjectProvenance {
108        private static final long serialVersionUID = 1L;
109
110        private final IntProvenance dimension;
111
112        ModHashCodeHasherProvenance(int dimension) {
113            this.dimension = new IntProvenance(DIMENSION,dimension);
114        }
115
116        public ModHashCodeHasherProvenance(Map<String, Provenance> map) {
117            dimension = ObjectProvenance.checkAndExtractProvenance(map,DIMENSION,IntProvenance.class,ModHashCodeHasherProvenance.class.getSimpleName());
118        }
119
120        @Override
121        public Map<String, Provenance> getConfiguredParameters() {
122            Map<String,Provenance> map = new HashMap<>();
123            map.put("saltStr",new StringProvenance("saltStr",""));
124            map.put(DIMENSION,dimension);
125            return map;
126        }
127
128        @Override
129        public boolean equals(Object o) {
130            if (this == o) return true;
131            if (!(o instanceof ModHashCodeHasherProvenance)) return false;
132            ModHashCodeHasherProvenance pairs = (ModHashCodeHasherProvenance) o;
133            return dimension.equals(pairs.dimension);
134        }
135
136        @Override
137        public int hashCode() {
138            return Objects.hash(dimension);
139        }
140
141        @Override
142        public String getClassName() {
143            return ModHashCodeHasher.class.getName();
144        }
145
146        @Override
147        public String toString() {
148            return generateString("Hasher");
149        }
150    }
151}