/*
* Copyright 2012 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.dht;
import java.io.IOException;
import java.security.PublicKey;
import java.util.Map.Entry;
import java.util.NavigableMap;
import java.util.TreeMap;
import net.tomp2p.peers.Number160;
import net.tomp2p.peers.Number640;
import net.tomp2p.storage.Data;
public class PutBuilder extends DHTBuilder<PutBuilder> {
private final static FuturePut FUTURE_SHUTDOWN = new FuturePut(null, 0, 0)
.failed("put builder - peer is shutting down");
private Entry<Number640, Data> data;
private NavigableMap<Number640, Data> dataMap;
private NavigableMap<Number160, Data> dataMapConvert;
private boolean putIfAbsent = false;
private boolean putMeta = false;
private boolean putConfirm = false;
private PublicKey changePublicKey = null;
public PutBuilder(PeerDHT peer, Number160 locationKey) {
super(peer, locationKey);
self(this);
}
public Entry<Number640, Data> data() {
return data;
}
public PutBuilder data(final Data data) {
return data(locationKey, domainKey == null ? Number160.ZERO : domainKey, Number160.ZERO,
versionKey == null ? Number160.ZERO : versionKey, data);
}
public PutBuilder data(final Number160 contentKey, final Data data) {
return data(locationKey, domainKey == null ? Number160.ZERO : domainKey, contentKey,
versionKey == null ? Number160.ZERO : versionKey, data);
}
public PutBuilder data(final Number160 domainKey, final Number160 contentKey, final Data data) {
return data(locationKey, domainKey, contentKey, versionKey == null ? Number160.ZERO : versionKey,
data);
}
public PutBuilder data(final Data data, final Number160 versionKey) {
return data(locationKey, domainKey == null ? Number160.ZERO : domainKey, Number160.ZERO,
versionKey, data);
}
public PutBuilder data(final Number160 contentKey, final Data data, final Number160 versionKey) {
return data(locationKey, domainKey == null ? Number160.ZERO : domainKey, contentKey, versionKey,
data);
}
public PutBuilder data(final Number160 locationKey, final Number160 domainKey,
final Number160 contentKey, final Number160 versionKey, final Data data) {
this.data = new Entry<Number640, Data>() {
@Override
public Data setValue(Data value) {
return null;
}
@Override
public Data getValue() {
return data;
}
@Override
public Number640 getKey() {
return new Number640(locationKey, domainKey, contentKey, versionKey);
}
};
return this;
}
@Override
public PutBuilder domainKey(final Number160 domainKey) {
// if we set data before we set domain key, we need to adapt the domain key of the data object
if (data != null) {
data(data.getKey().locationKey(), domainKey, data.getKey().contentKey(), data.getKey()
.versionKey(), data.getValue());
}
super.domainKey(domainKey);
return this;
}
@Override
public PutBuilder versionKey(final Number160 versionKey) {
// if we set data before we set domain key, we need to adapt the domain key of the data object
if (data != null) {
data(data.getKey().locationKey(), data.getKey().domainKey(), data.getKey()
.contentKey(), versionKey, data.getValue());
}
super.versionKey(versionKey);
return this;
}
public PutBuilder object(Object object) throws IOException {
return data(new Data(object));
}
public PutBuilder keyObject(Number160 contentKey, Object object) throws IOException {
return data(contentKey, new Data(object));
}
public NavigableMap<Number640, Data> dataMap() {
return dataMap;
}
public PutBuilder dataMap(NavigableMap<Number640, Data> dataMap) {
this.dataMap = dataMap;
return this;
}
public NavigableMap<Number160, Data> dataMapContent() {
return dataMapConvert;
}
public PutBuilder dataMapContent(NavigableMap<Number160, Data> dataMapConvert) {
this.dataMapConvert = dataMapConvert;
return this;
}
public boolean isPutIfAbsent() {
return putIfAbsent;
}
public PutBuilder putIfAbsent(boolean putIfAbsent) {
this.putIfAbsent = putIfAbsent;
return this;
}
public PutBuilder putIfAbsent() {
this.putIfAbsent = true;
return this;
}
public boolean isPutMeta() {
return putMeta;
}
public PutBuilder putMeta(boolean putMeta) {
this.putMeta = putMeta;
return this;
}
public PutBuilder putMeta() {
this.putMeta = true;
return this;
}
public boolean isPutConfirm() {
return putConfirm;
}
public PutBuilder putConfirm() {
this.putConfirm = true;
return this;
}
public PutBuilder changePublicKey(PublicKey changePublicKey) {
this.changePublicKey = changePublicKey;
this.putMeta = true;
sign();
return this;
}
public PublicKey changePublicKey() {
return changePublicKey;
}
public FuturePut start() {
if (peer.peer().isShutdown()) {
return FUTURE_SHUTDOWN;
}
preBuild("put-builder");
if (data != null) {
if (dataMap == null) {
dataMap(new TreeMap<Number640, Data>());
}
dataMap().put(data().getKey(), data().getValue());
}
if (!putMeta && !putConfirm && dataMap == null && dataMapConvert == null) {
throw new IllegalArgumentException(
"You must either set data via setDataMap() or setData(). Cannot add nothing.");
}
if (locationKey == null) {
throw new IllegalArgumentException("You must provide a location key.");
}
if (domainKey == null) {
domainKey = Number160.ZERO;
}
if (versionKey == null) {
versionKey = Number160.ZERO;
}
final FuturePut futurePut = new FuturePut(this, requestP2PConfiguration().minimumResults(), dataSize());
return peer.distributedHashTable().put(this, futurePut);
}
private int dataSize() {
if(isPutMeta() && changePublicKey()!=null) {
//we only send a marker
return 1;
} else if (isPutConfirm()) {
return 1;
} else if(dataMap()!=null) {
return dataMap().size();
} else {
return dataMapContent().size();
}
}
}