/*
* #!
* Ontopia Engine
* #-
* Copyright (C) 2001 - 2013 The Ontopia Project
* #-
* 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.ontopia.topicmaps.query.impl.utils;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* INTERNAL: Expected input is an array of maps with arbitrary keys and values of Object[] type.
*/
public class MultiCrossProduct {
protected int totsize;
protected int[] sizes;
protected int[] offsets;
protected Object[] keys;
protected Object[] values;
protected int[] indexes;
protected Object[] tuple;
protected int size = 1;
protected boolean finished;
public MultiCrossProduct(Map[] data) {
// Calculate total tuple widths
this.sizes = new int[data.length];
this.offsets = new int[data.length];
for (int i=0; i < data.length; i++) {
this.sizes[i] = data[i].size();
this.offsets[i] = this.totsize;
this.totsize += this.sizes[i];
}
// Initialize value tuple
this.tuple = new Object[this.totsize];
// Initialize keys array;
this.keys = new Object[this.totsize];
// Initialize indexes array;
this.indexes = new int[this.totsize];
// Intialize values array
this.values = new Object[this.totsize];
// Loop over input data
for (int i=0; i < data.length; i++) {
Object[] _keys = data[i].keySet().toArray();
// Sort keys array
Arrays.sort(_keys);
for (int i2=0; i2 < _keys.length; i2++) {
// Prepare key data
this.keys[offsets[i] + i2] = _keys[i2];
// Prepare value data
Object[] v = (Object[])data[i].get(_keys[i2]);
if (v == null || v.length == 0) {
finished = true;
size = 0;
} else {
size = size * v.length;
}
this.values[offsets[i] + i2] = v;
}
}
//! System.out.println("-> " + keys[i] + "=" + Arrays.asList((Object[])this.values[i]));
//! if (length == 0) finished = true;
}
public void reset() {
// Reset indexes and finished flag
if (size != 0) {
Arrays.fill(this.indexes, 0);
this.finished = false;
}
}
public int getSize() {
return size;
}
public Object[] getKeys() {
return keys;
}
public Object[] getKeys(int index) {
Object[] result = new Object[sizes[index]];
for (int i=0; i < result.length; i++) {
result[i] = keys[offsets[index]+i];
}
return result;
}
public Object[] getTuple() {
return tuple;
}
public Object[] getTuple(int index) {
Object[] result = new Object[sizes[index]];
for (int i=0; i < result.length; i++) {
result[i] = tuple[offsets[index]+i];
}
return result;
}
public Map getMap() {
Map result = new HashMap(keys.length);
for (int i=0; i < keys.length; i++) {
result.put(keys[i], tuple[i]);
}
return result;
}
public Map getMap(int index) {
Map result = new HashMap(sizes[index]);
for (int i=0; i < sizes[index]; i++) {
result.put(keys[offsets[index]+i], tuple[offsets[index]+i]);
}
return result;
}
public boolean nextTuple() {
if (finished) return false;
// Loop over indexes and use current index pointer
for (int i=0; i < tuple.length; i++) {
int index = indexes[i];
tuple[i] = ((Object[])values[i])[index];
}
// Increment index pointer (search backwards)
if (tuple.length == 0 )
finished = true;
else {
for (int i=tuple.length-1; i >= 0; i--) {
int index = indexes[i];
//! System.out.println(": " + index + " " + ((Object[])values[index]).length);
if (index+1 < ((Object[])values[i]).length) {
indexes[i]++;
//! System.out.println("INC: " + i + "=" + indexes[i] + " [" + ((Object[])values[i]).length + "]");
return true;
} else {
indexes[i] = 0;
if (i == 0) finished = true;
//! System.out.println("RES: " + i + "=" + indexes[i] + " [" + ((Object[])values[i]).length + "]");
}
}
}
return true;
}
public static void main(String args[]) {
Map data1 = new HashMap();
data1.put("A", new Object[] {"1", "2", "3"});
data1.put("B", new Object[] {"4"});
Map data2 = new HashMap();
data2.put("C", new Object[] {"5"});
data2.put("D", new Object[] {"6", "7"});
data2.put("E", new Object[] {"8", "9"});
//! data2.put("F", new Object[] {});
MultiCrossProduct cp = new MultiCrossProduct(new Map[] {data1, data2});
for (int i=1; i <= 3; i++) {
System.out.println(i + ": size " + cp.getSize());
while (cp.nextTuple()) {
System.out.print(Arrays.asList(cp.getKeys()).toString());
System.out.print(Arrays.asList(cp.getTuple()).toString());
System.out.print(cp.getMap(0).toString());
System.out.print(cp.getMap(1).toString());
System.out.println();
}
cp.reset();
}
}
}