/*******************************************************************************
*
* Copyright (c) 2004-2009 Oracle Corporation.
*
* 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:
*
* Kohsuke Kawaguchi
*
*
*******************************************************************************/
package hudson.matrix;
import java.util.List;
import java.util.ArrayList;
import java.util.AbstractList;
import java.util.Map;
import java.util.HashMap;
/**
* Used to assist thegeneration of config table.
*
* <p> {@link Axis Axes} are split into four groups. {@link #x Ones that are displayed as columns},
* {@link #y Ones that are displayed as rows},
* {@link #z Ones that are listed as bullet items inside table cell}, and those
* which only have one value, and therefore doesn't show up in the table.
*
* <p> Because of object reuse inside {@link Layouter}, this class is not
* thread-safe.
*
* @author Kohsuke Kawaguchi
*/
public abstract class Layouter<T> {
public final List<Axis> x, y, z;
/**
* Axes that only have one value.
*/
private final List<Axis> trivial = new ArrayList<Axis>();
/**
* Number of data columns and rows.
*/
private int xSize, ySize, zSize;
public Layouter(List<Axis> x, List<Axis> y, List<Axis> z) {
this.x = x;
this.y = y;
this.z = z;
init();
}
/**
* Automatically split axes to x,y, and z.
*/
public Layouter(AxisList axisList) {
x = new ArrayList<Axis>();
y = new ArrayList<Axis>();
z = new ArrayList<Axis>();
List<Axis> nonTrivialAxes = new ArrayList<Axis>();
for (Axis a : axisList) {
if (a.size() > 1) {
nonTrivialAxes.add(a);
} else {
trivial.add(a);
}
}
switch (nonTrivialAxes.size()) {
case 0:
break;
case 1:
z.add(nonTrivialAxes.get(0));
break;
case 2:
// use the longer axis in Y
Axis a = nonTrivialAxes.get(0);
Axis b = nonTrivialAxes.get(1);
x.add(a.size() > b.size() ? b : a);
y.add(a.size() > b.size() ? a : b);
break;
default:
// for size > 3, use x and y, and try to pack y more
for (int i = 0; i < nonTrivialAxes.size(); i++) {
(i % 3 == 1 ? x : y).add(nonTrivialAxes.get(i));
}
}
init();
}
private void init() {
xSize = calc(x, -1);
ySize = calc(y, -1);
zSize = calc(z, -1);
}
/**
* Computes the width of n-th X-axis.
*/
public int width(int n) {
return calc(x, n);
}
/**
* Computes the repeat count of n-th X-axis.
*/
public int repeatX(int n) {
int w = 1;
for (n--; n >= 0; n--) {
w *= x.get(n).size();
}
return w;
}
/**
* Computes the width of n-th Y-axis.
*/
public int height(int n) {
return calc(y, n);
}
private int calc(List<Axis> l, int n) {
int w = 1;
for (n++; n < l.size(); n++) {
w *= l.get(n).size();
}
return w;
}
/**
* Gets list of {@link Row}s to be displayed.
*
* The {@link Row} object is reused, so every value in collection returns
* the same object (but with different values.)
*/
public List<Row> getRows() {
return new AbstractList<Row>() {
final Row row = new Row();
public Row get(int index) {
row.index = index;
return row;
}
public int size() {
return ySize;
}
};
}
/**
* Represents a row, which is a collection of {@link Column}s.
*/
public final class Row extends AbstractList<Column> {
private int index;
final Column col = new Column();
@Override
public Column get(int index) {
col.xp = index;
col.yp = Row.this.index;
return col;
}
@Override
public int size() {
return xSize;
}
public String drawYHeader(int n) {
int base = calc(y, n);
if (index / base == (index - 1) / base && index != 0) {
return null; // no need to draw a new value
}
Axis axis = y.get(n);
return axis.value((index / base) % axis.values.size());
}
}
protected abstract T getT(Combination c);
public final class Column extends AbstractList<T> {
/**
* Cell position.
*/
private int xp, yp;
private final Map<String, String> m = new HashMap<String, String>();
public T get(int zp) {
m.clear();
buildMap(xp, x);
buildMap(yp, y);
buildMap(zp, z);
for (Axis a : trivial) {
m.put(a.name, a.value(0));
}
return getT(new Combination(m));
}
private void buildMap(int p, List<Axis> axes) {
int n = p;
for (int i = axes.size() - 1; i >= 0; i--) {
Axis a = axes.get(i);
m.put(a.name, a.value(n % a.size()));
n /= a.size();
}
}
public int size() {
return zSize;
}
}
}