/******************************************************************************* * Copyright 2010 Cees De Groot, Alex Boisvert, 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 de.mxro.thrd.jdbm2V22.helper; import java.io.IOError; import java.io.IOException; import java.util.ArrayList; import java.util.Comparator; import java.util.Iterator; import java.util.List; import de.mxro.thrd.jdbm2V22.*; import de.mxro.thrd.jdbm2V22.btree.BTree; import de.mxro.thrd.jdbm2V22.btree.BTreeSecondarySortedMap; import de.mxro.thrd.jdbm2V22.htree.HTree; import de.mxro.thrd.jdbm2V22.htree.HTreeSecondaryMap; /** * Utilities related to Secondary Maps * * @author Jan Kotek * */ final public class SecondaryKeyHelper { static public <A,K,V> BTree<A,Iterable<K>> secondaryBTree(String objectName, final SecondaryKeyExtractor<A,K,V> keyExtractor, Comparator<A> comparator, JdbmBase<K,V> b) throws IOException{ BTree<A,Iterable<K>> secIndex = null; long recid = b.getRecordManager().getNamedObject( objectName ); if ( recid != 0 ) { secIndex = BTree.load(b.getRecordManager(), recid ); } else { secIndex = BTree.createInstance( b.getRecordManager(), comparator ); b.getRecordManager().setNamedObject( objectName, secIndex.getRecid() ); } //second final variable so it can be accesed from listener final BTree<A,Iterable<K>> secIndex2 = secIndex; b.addRecordListener(new RecordListener<K, V>() { public void recordInserted(K key, V value) throws IOException { A secKey = keyExtractor.extractSecondaryKey(key,value); if(secKey == null) return; List<K> kk = (List<K>) secIndex2.find(secKey); if(kk == null) kk = new ArrayList<K>(); kk.add(key); secIndex2.insert(secKey, kk, true); } public void recordRemoved(K key, V value) throws IOException { A secKey = keyExtractor.extractSecondaryKey(key,value); List<K> kk = (List<K>) secIndex2.find(secKey); if(kk == null) return; kk.remove(key); if(kk.isEmpty()) secIndex2.remove(secKey); else secIndex2.insert(secKey, kk, true); } public void recordUpdated(K key, V oldValue, V newValue) throws IOException { A oldSecKey = keyExtractor.extractSecondaryKey(key, oldValue); A newSecKey = keyExtractor.extractSecondaryKey(key, newValue); if(oldSecKey==null && newSecKey == null) return; if(oldSecKey==null && newSecKey!=null){ //insert new record recordInserted(key, newValue); return; } if(oldSecKey!=null && newSecKey==null){ //delete old record recordRemoved(key, oldValue); return; } if(oldSecKey.equals(newSecKey)) //both keys are equal, nothing return; //remove old key recordRemoved(key, oldValue); //insert new key recordInserted(key,newValue); } }); return secIndex; } static public <A,K,V> HTree<A,Iterable<K>> secondaryHTree(String objectName, final SecondaryKeyExtractor<A,K,V> keyExtractor, JdbmBase<K,V> b,Serializer<A> secondaryKeySerializer) throws IOException{ HTree<A,Iterable<K>> secIndex = null; long recid = b.getRecordManager().getNamedObject( objectName ); if ( recid != 0 ) { secIndex = HTree.load(b.getRecordManager(), recid,secondaryKeySerializer,null ); } else { secIndex = HTree.createInstance( b.getRecordManager(),secondaryKeySerializer,null); b.getRecordManager().setNamedObject( objectName, secIndex.getRecid() ); } //second final variable so it can be accesed from listener final HTree<A,Iterable<K>> secIndex2 = secIndex; b.addRecordListener(new RecordListener<K, V>() { public void recordInserted(K key, V value) throws IOException { A secKey = keyExtractor.extractSecondaryKey(key,value); if(secKey == null) return; List<K> kk = (List<K>) secIndex2.find(secKey); if(kk == null) kk = new ArrayList<K>(); kk.add(key); secIndex2.put(secKey, kk); } public void recordRemoved(K key, V value) throws IOException { A secKey = keyExtractor.extractSecondaryKey(key,value); List<K> kk = (List<K>) secIndex2.find(secKey); if(kk == null) return; kk.remove(key); if(kk.isEmpty()) secIndex2.remove(secKey); else secIndex2.put(secKey, kk); } public void recordUpdated(K key, V oldValue, V newValue) throws IOException { A oldSecKey = keyExtractor.extractSecondaryKey(key,oldValue); A newSecKey = keyExtractor.extractSecondaryKey(key,newValue); if(oldSecKey==null && newSecKey == null) return; if(oldSecKey==null && newSecKey!=null){ //insert new record recordInserted(key, newValue); return; } if(oldSecKey!=null && newSecKey==null){ //delete old record recordRemoved(key, oldValue); return; } if(oldSecKey.equals(newSecKey)) //both keys are equal, nothing return; //remove old key recordRemoved(key, oldValue); //insert new key recordInserted(key,newValue); } }); return secIndex; } static public <A,K,V> BTree<A,Iterable<K>> secondaryBTreeManyToOne(String objectName, final SecondaryKeyExtractor<Iterable<A>,K,V> keyExtractor, Comparator<A> comparator, JdbmBase<K,V> b) throws IOException{ BTree<A,Iterable<K>> secIndex = null; long recid = b.getRecordManager().getNamedObject( objectName ); if ( recid != 0 ) { secIndex = BTree.load(b.getRecordManager(), recid ); } else { secIndex = BTree.createInstance( b.getRecordManager(), comparator ); b.getRecordManager().setNamedObject( objectName, secIndex.getRecid() ); } //second final variable so it can be accesed from listener final BTree<A,Iterable<K>> secIndex2 = secIndex; b.addRecordListener(new RecordListener<K, V>() { public void recordInserted(K key, V value) throws IOException { for(A secKey : keyExtractor.extractSecondaryKey(key,value)){ if(secKey == null) return; List<K> kk = (List<K>) secIndex2.find(secKey); if(kk == null) kk = new ArrayList<K>(); kk.add(key); secIndex2.insert(secKey, kk, true); } } public void recordRemoved(K key, V value) throws IOException { for(A secKey : keyExtractor.extractSecondaryKey(key,value)){ List<K> kk = (List<K>) secIndex2.find(secKey); if(kk == null) return; kk.remove(key); if(kk.isEmpty()) secIndex2.remove(secKey); else secIndex2.insert(secKey, kk, true); } } public void recordUpdated(K key, V oldValue, V newValue) throws IOException { Iterable<A> oldSecKey = keyExtractor.extractSecondaryKey(key, oldValue); Iterable<A> newSecKey = keyExtractor.extractSecondaryKey(key, newValue); if(oldSecKey==null && newSecKey == null) return; if(oldSecKey==null && newSecKey!=null){ //insert new record recordInserted(key, newValue); return; } if(oldSecKey!=null && newSecKey==null){ //delete old record recordRemoved(key, oldValue); return; } if(oldSecKey.equals(newSecKey)) //both keys are equal, nothing return; //remove old key recordRemoved(key, oldValue); //insert new key recordInserted(key,newValue); } }); return secIndex; } static public <A,K,V> HTree<A,Iterable<K>> secondaryHTreeManyToOne(String objectName, final SecondaryKeyExtractor<Iterable<A>,K,V> keyExtractor, JdbmBase<K,V> b,Serializer<A> secondaryKeySerializer) throws IOException{ HTree<A,Iterable<K>> secIndex = null; long recid = b.getRecordManager().getNamedObject( objectName ); if ( recid != 0 ) { secIndex = HTree.load(b.getRecordManager(), recid,secondaryKeySerializer,null ); } else { secIndex = HTree.createInstance( b.getRecordManager(),secondaryKeySerializer,null); b.getRecordManager().setNamedObject( objectName, secIndex.getRecid() ); } //second final variable so it can be accesed from listener final HTree<A,Iterable<K>> secIndex2 = secIndex; b.addRecordListener(new RecordListener<K, V>() { public void recordInserted(K key, V value) throws IOException { for(A secKey : keyExtractor.extractSecondaryKey(key,value)){ if(secKey == null) return; List<K> kk = (List<K>) secIndex2.find(secKey); if(kk == null) kk = new ArrayList<K>(); kk.add(key); secIndex2.put(secKey, kk); } } public void recordRemoved(K key, V value) throws IOException { for(A secKey : keyExtractor.extractSecondaryKey(key,value)){ List<K> kk = (List<K>) secIndex2.find(secKey); if(kk == null) return; kk.remove(key); if(kk.isEmpty()) secIndex2.remove(secKey); else secIndex2.put(secKey, kk); } } public void recordUpdated(K key, V oldValue, V newValue) throws IOException { Iterable<A> oldSecKey = keyExtractor.extractSecondaryKey(key,oldValue); Iterable<A> newSecKey = keyExtractor.extractSecondaryKey(key,newValue); if(oldSecKey==null && newSecKey == null) return; if(oldSecKey==null && newSecKey!=null){ //insert new record recordInserted(key, newValue); return; } if(oldSecKey!=null && newSecKey==null){ //delete old record recordRemoved(key, oldValue); return; } if(oldSecKey.equals(newSecKey)) //both keys are equal, nothing return; //remove old key recordRemoved(key, oldValue); //insert new key recordInserted(key,newValue); } }); return secIndex; } public static <A,K,V> SecondaryHashMap<A,K,V> secondaryHashMap( String objectName, SecondaryKeyExtractor<A, K, V> secKeyExtractor, JdbmBase<K,V> b, Serializer<A> secondaryKeySerializer){ try{ HTree<A,Iterable<K>> secTree = secondaryHTree(objectName, secKeyExtractor, b,secondaryKeySerializer); HTreeSecondaryMap<A, K, V> ret = new HTreeSecondaryMap<A, K, V>(secTree, b); return ret; }catch (IOException e){ throw new IOError(e); } } public static <A,K,V> SecondaryTreeMap<A,K,V> secondaryTreeMap( String objectName, SecondaryKeyExtractor<A, K, V> secKeyExtractor, Comparator<A> comparator, JdbmBase<K,V> b, Serializer<A> secondaryKeySerializer){ try{ BTree<A,Iterable<K>> secTree = secondaryBTree(objectName, secKeyExtractor, comparator,b); if(secondaryKeySerializer!=null) secTree.setKeySerializer(secondaryKeySerializer); BTreeSecondarySortedMap<A, K, V> ret = new BTreeSecondarySortedMap<A, K, V>(secTree, b); return ret; }catch (IOException e){ throw new IOError(e); } } public static <A,K,V> SecondaryHashMap<A,K,V> secondaryHashMapManyToOne( String objectName, SecondaryKeyExtractor<Iterable<A>, K, V> secKeyExtractor, JdbmBase<K,V> b, Serializer<A> secondaryKeySerializer){ try{ HTree<A,Iterable<K>> secTree = secondaryHTreeManyToOne(objectName, secKeyExtractor, b,secondaryKeySerializer); HTreeSecondaryMap<A, K, V> ret = new HTreeSecondaryMap<A, K, V>(secTree, b); return ret; }catch (IOException e){ throw new IOError(e); } } public static <A,K,V> SecondaryTreeMap<A,K,V> secondarySortedMapManyToOne( String objectName, SecondaryKeyExtractor<Iterable<A>, K, V> secKeyExtractor, Comparator<A> comparator, JdbmBase<K,V> b,Serializer<A> secondaryKeySerializer){ try{ BTree<A,Iterable<K>> secTree = secondaryBTreeManyToOne(objectName, secKeyExtractor, comparator,b); if(secondaryKeySerializer!=null) secTree.setKeySerializer(secondaryKeySerializer); BTreeSecondarySortedMap<A, K, V> ret = new BTreeSecondarySortedMap<A, K, V>(secTree, b); return ret; }catch (IOException e){ throw new IOError(e); } } @SuppressWarnings("unchecked") public static <K,V> InverseHashView<K, V> inverseHashView(JdbmBase<K,V> base, String recordName){ SecondaryKeyExtractor<Integer,K,V> hashExtractor = new SecondaryKeyExtractor<Integer, K, V>() { long hashEqualsIdentityCounter=0; public Integer extractSecondaryKey(K key, V value) { int hashCode = value.hashCode(); //little check to protect from hashCode not being overriden int identityHashCode = System.identityHashCode(value); if(hashCode == identityHashCode) hashEqualsIdentityCounter++; else if(hashEqualsIdentityCounter>0) hashEqualsIdentityCounter--; //fail if too many objects had hash equal to identityHashCode if(hashEqualsIdentityCounter>50) throw new IllegalArgumentException("Object does not implement hashCode() correctly: "+value.getClass()); return new Integer(hashCode); } }; final SecondaryTreeMap<Integer, K, V> inverseMap = secondaryTreeMap(recordName,hashExtractor,ComparableComparator.INSTANCE,base,null); return new InverseHashView<K, V>() { public K findKeyForValue(V val) { Iterable<K> keys = inverseMap.get(new Integer(val.hashCode())); if(keys == null) return null; for(K k:keys){ if(val.equals(inverseMap.getPrimaryValue(k))) return k; } return null; } public Iterable<K> findKeysForValue(V val) { Iterable<K> keys = inverseMap.get(new Integer(val.hashCode())); List<K> ret = new ArrayList<K>(); if(keys == null) return null; for(K k:keys){ if(val.equals(inverseMap.getPrimaryValue(k))) ret.add(k); } return ret; } }; } public static <K,V> Iterable<V> translateIterable(final JdbmBase<K, V> b, final Iterable<K> keys){ if(keys==null) return new ArrayList<V>(); return new Iterable<V>(){ public Iterator<V> iterator() { return new Iterator<V>(){ Iterator<K> iter = keys.iterator(); public boolean hasNext() { return iter.hasNext(); } public V next() { try { return b.find(iter.next()); } catch (IOException e) { throw new IOError(e); } } public void remove() { iter.remove(); }}; } }; } }