/*
* Copyright 2009 Thomas Bocek
*
* 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.
*/
package net.tomp2p.rpc;
import java.util.Collection;
import java.util.Map;
import java.util.NavigableMap;
import java.util.TreeMap;
import net.tomp2p.peers.Number160;
import net.tomp2p.peers.Number640;
/**
* Calculates or sets a global hash. The digest is used in two places: for routing, where a message needs to have a
* predictable size. Thus in this case a global hash is calculated. The second usage is get() for getting a list of
* hashes from peers. Here we don't need to restrict ourself, since we use TCP.
*
* @author Thomas Bocek
*/
public class DigestInfo {
private volatile Number160 keyDigest = null;
private volatile Number160 contentDigest = null;
private volatile int size = -1;
private final NavigableMap<Number640, Collection<Number160>> mapDigests = new TreeMap<Number640, Collection<Number160>>();
/**
* Empty constructor is used to add the hashes to the list.
*/
public DigestInfo() {
}
/**
* Creates a digest with the size only.
*
* @param size
* The number of items
*/
public DigestInfo(final int size) {
this.size = size;
}
/**
* If a global hash has already been calculated, then this constructor is used to store those. Note that once a
* global hash is set it cannot be unset.
*
* @param keyDigest
* The digest of all keys
* @param contentDigest
* The digest of all contents
* @param size
* The number of entries
*/
public DigestInfo(final Number160 keyDigest, final Number160 contentDigest, final int size) {
this.keyDigest = keyDigest;
this.contentDigest = contentDigest;
this.size = size;
}
/**
* @return Returns or calculates the global key hash. The global key hash will be calculated if the empty
* constructor is used.
*/
public Number160 keyDigest() {
if (keyDigest == null) {
process();
}
return keyDigest;
}
/**
* @return Returns or calculates the global content hash. The global content hash will be calculated if the empty
* constructor is used.
*/
public Number160 contentDigest() {
if (contentDigest == null) {
process();
}
return contentDigest;
}
/**
* Calculates the digest.
*/
private void process() {
Number160 hashKey = Number160.ZERO;
Number160 hashContent = Number160.ZERO;
for (Map.Entry<Number640, Collection<Number160>> entry : mapDigests.entrySet()) {
hashKey = hashKey.xor(entry.getKey().locationKey());
hashKey = hashKey.xor(entry.getKey().domainKey());
hashKey = hashKey.xor(entry.getKey().contentKey());
hashKey = hashKey.xor(entry.getKey().versionKey());
for (Number160 basedOn: entry.getValue()) {
hashContent = hashContent.xor(basedOn);
}
}
keyDigest = hashKey;
contentDigest = hashContent;
}
public NavigableMap<Number640, Collection<Number160>> mapDigests() {
return mapDigests;
}
/**
* Stores a key and the hash of the content for further processing.
*
* @param key
* The key of the content
* @param basedOnSet
* The hash of the content
*/
public void put(final Number640 key, final Collection<Number160> basedOnSet) {
mapDigests.put(key, basedOnSet);
}
/**
* @return The list of hashes
*/
public NavigableMap<Number640, Collection<Number160>> digests() {
return mapDigests;
}
/**
* @return The number of hashes
*/
public int size() {
if (size == -1) {
size = mapDigests.size();
}
return size;
}
/**
* @return True, if the digest information has not been provided.
*/
public boolean isEmpty() {
return size <= 0;
}
@Override
public boolean equals(final Object obj) {
if (!(obj instanceof DigestInfo)) {
return false;
}
if (this == obj) {
return true;
}
DigestInfo other = (DigestInfo) obj;
return keyDigest().equals(other.keyDigest()) && size() == other.size()
&& contentDigest().equals(other.contentDigest());
}
@Override
public int hashCode() {
return keyDigest().hashCode() ^ size() ^ contentDigest().hashCode();
}
}