/*
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
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;
}
}
}