Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright (c) 2012 Jan Kotek
*
* 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 org.mapdb20;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
/**
* Binding is simple yet powerful way to keep secondary collection synchronized with primary collection.
* Primary collection provides notification on updates and secondary collection is modified accordingly.
* This way MapDB provides secondary indexes, values and keys. It also supports less usual scenarios such
* as histograms, inverse lookup index (on maps), group counters and so on.
*
* There are two things to keep on mind when using binding:
*
* * Binding is not persistent, so it needs to be restored every time store is reopened.
* If you modify primary collection before binding is restored, secondary collection does not get updated and becomes
* inconsistent.
*
* * If secondary collection is empty, binding will recreate its content based on primary collection.
* If there is even single item on secondary collection, binding assumes it is consistent and leaves it as its.
*
* Any thread-safe collection can be used as secondary (not just collections provided by MapDB).
* This gives great flexibility for modeling
* and scaling your data. For example primary data can be stored in durable DB with transactions and large secondary
* indexes may be stored in other faster non-durable DB. Or primary collection may be stored on disk and smaller
* secondary index (such as category counters) can be stored in memory for faster lookups. Also you may use
* ordinary {@code java.util.*} collections (if they are thread safe) to get additional speed.
*
* There are many [code examples](https://github.com/jankotek/MapDB/tree/master/src/test/java/examples)
* how Collection Binding can be used.
*
* NOTE: Binding just installs Modification Listener on primary collection. Binding itself is not persistent
* and has to be restored after primary collection is loaded. Data contained in secondary collection are persistent.
*
*
* @author Jan Kotek
*/
public final class Bind {
private Bind(){}
/**
* Listener called when {@code Map} is modified.
* @param key type in map
* @param value type in map
*/
public interface MapListener{
/**
* Callback method called after {@code Map} was modified.
* It is called on insert, update or delete.
*
* MapDB collections do not support null keys or values.
* Null parameter may be than used to indicate operation:
*
*
*
* @param key key in map
* @param oldVal old value in map (if any, null on inserts)
* @param newVal new value in map (if any, null on deletes)
*/
void update(K key, V oldVal, V newVal);
}
/**
* Primary Maps must provide notifications when it is modified.
* So Primary Maps must implement this interface to allow registering callback listeners.
*
* @param key type in map
* @param value type in map
*/
public interface MapWithModificationListener extends ConcurrentMap {
/**
* Add new modification listener notified when Map has been updated
* @param listener callback interface notified when map changes
*/
public void modificationListenerAdd(MapListener listener);
/**
* Remove registered notification listener
*
* @param listener callback interface notified when map changes
*/
public void modificationListenerRemove(MapListener listener);
/**
*
* @return size of map, but in 64bit long which does not overflow at 2e9 items.
*/
public long sizeLong();
}
/**
* Binds {@link Atomic.Long} to Primary Map so the Atomic.Long contains size of Map.
* {@code Atomic.Long} is incremented on each insert and decremented on each entry removal.
* MapDB collections usually do not keep their size, but require complete traversal to count items.
*
* If {@code Atomic.Long} has zero value, it will be updated with value from {@code map.size()} and than
* bind to map.
*
* NOTE: Binding just installs Modification Listener on primary collection. Binding itself is not persistent
* and has to be restored after primary collection is loaded. Data contained in secondary collection are persistent.
*
*
* NOTE: {@link BTreeMap} and {@link HTreeMap} already supports this directly as optional parameter named {@code counter}.
* In that case all calls to {@code Map.size()} are forwarded to underlying counter. Check parameters at
* {@link DB#hashMapCreate(String)} and
* {@link DB#treeMapCreate(String)}
*
* @param type of key in map
* @param type of value in map
* @param map primary map whose size needs to be tracked
* @param sizeCounter number updated when Map Entry is added or removed.
*/
public static void size(MapWithModificationListener map, final Atomic.Long sizeCounter){
//set initial value first if necessary
//$DELAY$
if(sizeCounter.get() == 0){
//$DELAY$
long size = map.sizeLong();
if(sizeCounter.get()!=size) {
//$DELAY$
sizeCounter.set(size);
//$DELAY$
}
}
map.modificationListenerAdd(new MapListener() {
@Override
public void update(K key, V oldVal, V newVal) {
//$DELAY$
if (oldVal == null && newVal != null) {
//$DELAY$
sizeCounter.incrementAndGet();
} else if (oldVal != null && newVal == null) {
//$DELAY$
sizeCounter.decrementAndGet();
}
//$DELAY$
//update does not change collection size
}
});
}
/**
* Binds Secondary Map so that it contains Key from Primary Map and custom Value.
* Secondary Value is updated every time Primary Map is modified.
*
* If Secondary Map is empty its content will be recreated from Primary Map.
* This binding is not persistent. You need to restore it every time store is reopened.
*
*
* NOTE: Binding just installs Modification Listener on primary collection. Binding itself is not persistent
* and has to be restored after primary collection is loaded. Data contained in secondary collection are persistent.
*
* Type params:
*
* @param - key type in primary and Secondary Map
* @param - value type in Primary Map
* @param - value type in Secondary Map
*
* @param map Primary Map
* @param secondary Secondary Map with custom
* @param fun function which calculates secondary value from primary key and value
*/
public static void secondaryValue(MapWithModificationListener map,
final Map secondary,
final Fun.Function2 fun){
//$DELAY$
//fill if empty
if(secondary.isEmpty()){
//$DELAY$
for(Map.Entry e:map.entrySet())
secondary.put(e.getKey(), fun.run(e.getKey(),e.getValue()));
}
//$DELAY$
//hook listener
map.modificationListenerAdd(new MapListener() {
@Override
public void update(K key, V oldVal, V newVal) {
//$DELAY$
if (newVal == null) {
//removal
secondary.remove(key);
//$DELAY$
} else {
//$DELAY$
secondary.put(key, fun.run(key, newVal));
}
//$DELAY$
}
});
}
/**
* Binds Secondary Map so that it contains Key from Primary Map and custom Value.
* Secondary Value is updated every time Primary Map is modified.
*
* If Secondary Map is empty its content will be recreated from Primary Map.
* This binding is not persistent. You need to restore it every time store is reopened.
*
*
* NOTE: Binding just installs Modification Listener on primary collection. Binding itself is not persistent
* and has to be restored after primary collection is loaded. Data contained in secondary collection are persistent.
*
* Type params:
*
* @param - key type in primary and Secondary Map
* @param - value type in Primary Map
* @param - value type in Secondary Map
* .
* @param map Primary Map
* @param secondary Secondary Map with custom
* @param fun function which calculates secondary values from primary key and value
*/
public static void secondaryValues(MapWithModificationListener map,
final Set