/*******************************************************************************
* Copyright (c) 2007, 2010 Association for Decentralized Information Management
* in Industry THTH ry.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* VTT Technical Research Centre of Finland - initial API and implementation
*******************************************************************************/
package org.simantics.utils.datastructures.map;
import gnu.trove.map.hash.THashMap;
import gnu.trove.set.hash.THashSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
/**
* Associative map of n-tuples. Tuple is the primitive of this data structure.
* It describes an association between objects.
* The number of fields in each tuple must match the level of associativity.
*
* Associative map allows quick queries of tuples by pre-indexing its content.
* Indexing requires that the object is told ahead which combinations of fields
* to cache. Each combination is described with {@link Associativity} object
* at construction, and for each description a new hashmap is created.
*
* Example, fully associative map for 4-tuples, use
*
* AssociativeMap map = new AssociativeMap( Associativity.fullAssociativity( 4 ) );
* map.addStatements( new Tuple( cluster, subject, predicate, object ) );
*
* Example, BijectionMap
*
* AssociativeMap bijectionMap =
* new AssociativeMap( new Associativity(false, true), new Associativity(true, false) )
*
* bijectionMap.addStatements( new Tuple("x", "y") );
*
* Queries are described with {@link Tuple}-instances. null value describes
* open ended value and actual object restriction.
*
* Example, query for all Tuples with "x" as the first argument:
*
* bijectionMap.getStatements(new Tuple("x", null), null);;
*
* @see Associativity
* @see Tuple
* @author Toni Kalajainen
*/
public class AssociativeMap {
/** Number of dimensions */
int level;
/** Maps sorted by TupleQueryAssociativity, TupleQuery, Tuples */
THashMap> [] maps;
/** Tuple query for all statements */
Tuple tupleQueryAllTuples;
/** Associativities */
Associativity[] associativities;
/**
* Create partially associative map of n-tuples.
*
* @param associativenesses
*/
@SuppressWarnings("unchecked")
public AssociativeMap(Associativity ... associativenesses)
{
if (associativenesses==null) throw new NullPointerException();
if (associativenesses.length==0) throw new IllegalArgumentException();
this.associativities = associativenesses;
level = associativenesses[0].getLevel();
for (int i=1; i> map = new THashMap>();
maps[associativity] = map;
}
tupleQueryAllTuples = new Tuple(new Object[level]);
}
public void add(Collection stms)
{
for (int associativity=0; associativity> map = maps[associativity];
if (map==null) continue;
for (Tuple stm : stms) {
if (!stm.isFull()) throw new IllegalArgumentException("Tuple Query cannot be added.");
Tuple tupleQuery = createTupleQuery(stm, associativity);
THashSet set = map.get(tupleQuery);
if (set==null) map.put(tupleQuery, set = new THashSet());
set.add(stm);
}
}
}
public void add(Tuple...stms)
{
for (int associativity=0; associativity> map = maps[associativity];
if (map==null) continue;
for (Tuple stm : stms) {
if (!stm.isFull()) throw new IllegalArgumentException("Tuple Query cannot be added.");
Tuple tupleQuery = createTupleQuery(stm, associativity);
THashSet set = map.get(tupleQuery);
if (set==null) map.put(tupleQuery, set = new THashSet());
set.add(stm);
}
}
}
public void remove(Collection stms) {
for (int associativity=0; associativity> map = maps[associativity];
if (map==null) continue;
for (Tuple stm : stms) {
Tuple tupleQuery = createTupleQuery(stm, associativity);
THashSet set = map.get(tupleQuery);
if (set==null) continue;
set.remove(stm);
}
}
}
public Collection get(Tuple tupleQuery, Collection result)
throws UnsupportedOperationException
{
if (tupleQuery ==null || tupleQuery.getLevel() != level) throw new IllegalArgumentException();
/** Special case, query for existance of a specific tuple */
if (tupleQuery.associativity == maps.length) {
THashMap> map = maps[0];
if (map==null) throw new UnsupportedOperationException();
THashSet set = map.get(tupleQueryAllTuples);
if (result == null) {
if (set!=null && set.contains(tupleQuery))
return Collections.singleton(tupleQuery);
else
return Collections.emptySet();
} else {
if (set.contains(tupleQuery))
result.add(tupleQuery);
return result;
}
}
THashMap> map = maps[tupleQuery.associativity];
if (map==null) throw new UnsupportedOperationException();
THashSet set = map.get(tupleQuery);
if (set==null) return Collections.emptySet();
if (result == null) result = new ArrayList(set.size());
for (Tuple t : set)
result.add(t);
return result;
}
public Associativity[] getAssociativities() {
return associativities;
}
private static Tuple createTupleQuery(Tuple tuple, int associativity)
{
if (tuple.associativity == associativity) return tuple;
int mask = 1;
int level = tuple.getLevel();
Object fields[] = new Object[level];
for (int i=0; i0)
fields[i] = tuple.getField(i);
mask <<= 1;
}
return new Tuple(fields);
}
public int getLevel() {
return level;
}
}