/* * Copyright: Almende B.V. (2014), Rotterdam, The Netherlands * License: The Apache Software License, Version 2.0 */ package com.almende.dht; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.BitSet; import java.util.Random; import java.util.logging.Logger; /** * The Class Key. */ public final class Key implements Comparable<Key> { private static final Logger LOG = Logger.getLogger(Key.class.getName()); private BitSet val; /** * Instantiates a new key. * * @param val * the val */ public Key(final BitSet val) { this.val = val; } /** * Instantiates a new key. */ public Key() { this.val = new BitSet(Constants.BITLENGTH); } /** * Instantiates a new key. * * @param key * the key */ public Key(final String key) { this.val = BitSet.valueOf(hexToBytes(key)); } /** * Random. * * @return the key */ public static Key random() { final BitSet set = new BitSet(Constants.BITLENGTH); final Random rand = new Random(); for (int i = 0; i < Constants.BITLENGTH; i++) { set.set(i, rand.nextBoolean()); } return new Key(set); } /** * Random key of rank X. * * @param rank * the rank * @return the key */ public static Key random(int rank) { if (rank > Constants.BITLENGTH) { LOG.warning("Rank too high!"); rank = Constants.BITLENGTH; } final BitSet set = new BitSet(Constants.BITLENGTH); final Random rand = new Random(); for (int i = 0; i < rank - 2; i++) { set.set(i, rand.nextBoolean()); } set.set(rank - 1); return new Key(set); } /** * Digest. * * @param val * the val * @return the key */ public static Key digest(final String val) { try { MessageDigest md = MessageDigest.getInstance("SHA1"); return new Key(BitSet.valueOf(md.digest(val.getBytes("UTF-8")))); } catch (NoSuchAlgorithmException e) { LOG.severe("SHA1 unknown???!"); } catch (UnsupportedEncodingException e) { LOG.severe("UTF-8 unknown???!"); } return null; } /** * From hex string. * * @param val * the val * @return the key */ public static Key fromHexString(final String val) { return new Key(val); } /** * From string. * * @param val * the val * @return the key */ public static Key fromString(final String val) { return Key.digest(val); } /** * Gets the val. * * @return the val */ public BitSet getVal() { return val; } /** * Sets the val. * * @param val * the new val */ public void setVal(BitSet val) { this.val = val; } /** * Rank. * * @return the int */ public int rank() { return val.length(); } /** * Dist. * * @param o * the o * @return the key */ public Key dist(final Key o) { final BitSet res = ((BitSet) val.clone()); res.xor(o.val); return new Key(res); } /* * (non-Javadoc) * @see java.lang.Object#hashCode() */ @Override public int hashCode() { return val.hashCode(); } /* * (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(final Object o) { if (o == null) { return false; } if (!(o instanceof Key)) { return false; } if (o == this) { return true; } final Key other = (Key) o; return (val.equals(other.val)); } /* * (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { return bytesToHex(val.toByteArray()); } // From: http://stackoverflow.com/a/9855338 final private static char[] hexArray = "0123456789ABCDEF".toCharArray(); private static String bytesToHex(byte[] bytes) { char[] hexChars = new char[bytes.length * 2]; for (int j = 0; j < bytes.length; j++) { int v = bytes[j] & 0xFF; hexChars[j * 2] = hexArray[v >>> 4]; hexChars[j * 2 + 1] = hexArray[v & 0x0F]; } return new String(hexChars); } // From: http://stackoverflow.com/a/140861 private static byte[] hexToBytes(String s) { int len = s.length(); if (len % 2 == 1) { throw new IllegalArgumentException("HexString uneven length:'" + s + "'"); } byte[] data = new byte[len / 2]; for (int i = 0; i < len; i += 2) { data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character .digit(s.charAt(i + 1), 16)); } return data; } /* * (non-Javadoc) * @see java.lang.Comparable#compareTo(java.lang.Object) */ @Override public int compareTo(Key o) { if (this.equals(o)) { return 0; } if (rank() > o.rank()) { return 1; } if (rank() < o.rank()) { return -1; } int index = dist(o).rank() - 1; return val.get(index) ? 1 : -1; } }