All Downloads are FREE. Search and download functionalities are using the official Maven repository.

hudson.matrix.Combination Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 *
 * 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, Winston Prakash
 *
 *******************************************************************************/ 

package hudson.matrix;

import hudson.Util;
import hudson.model.Hudson;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeMap;
import static java.lang.Boolean.TRUE;

/**
 * A particular combination of {@link Axis} values.
 *
 * For example, when axes are "x={1,2},y={3,4}", then [x=1,y=3] is a combination
 * (out of 4 possible combinations)
 *
 * @author Kohsuke Kawaguchi
 */
public final class Combination extends TreeMap implements Comparable {

    protected static final String DELIM = ",";

    public Combination(AxisList axisList, List values) {
        for (int i = 0; i < axisList.size(); i++) {
            super.put(axisList.get(i).getName(), values.get(i));
        }
    }

    public Combination(AxisList axisList, String... values) {
        this(axisList, Arrays.asList(values));
    }

    public Combination(Map keyValuePairs) {
        for (Map.Entry e : keyValuePairs.entrySet()) {
            super.put(e.getKey(), e.getValue());
        }
    }

    public String get(Axis a) {
        return get(a.getName());
    }

    /**
     * Obtains the continuous unique index number of this {@link Combination} in
     * the given {@link AxisList}.
     */
    public int toIndex(AxisList axis) {
        int r = 0;
        for (Axis a : axis) {
            r *= a.size();
            r += a.indexOf(get(a));
        }
        return r;
    }

    /**
     * Obtains a number N such that "N%M==0" would create a reasonable sparse
     * matrix for integer M.
     *
     * 

This is bit like {@link #toIndex(AxisList)}, but instead of creating * a continuous number (which often maps different values of the same axis * to the same index in modulo N residue ring, we use a prime number P as * the base. I think this guarantees the uniform distribution in any N * smaller than 2P (but proof, anyone?) */ private long toModuloIndex(AxisList axis) { long r = 0; for (Axis a : axis) { r += a.indexOf(get(a)); r *= 31; } return r; } /** * Evaluates the given Dynamic Language Script Expression with values bound * from this combination.

For example, if this combination is a=X,b=Y, * then expressions like a=="X" would evaluate to true. * * @param axes * @param expression * @param scriptType * @return */ public boolean evalScriptExpression(AxisList axes, String expression) { if (Util.fixEmptyAndTrim(expression) == null) { return true; } Object result = Boolean.TRUE; if (Hudson.getInstance().getScriptSupport() != null) { expression = "use(" + BooleanCategory.class.getName().replace('$', '.') + ") {" + expression + "}"; Map variableMap = new HashMap(); for (Map.Entry e : entrySet()) { variableMap.put(e.getKey(), e.getValue()); } variableMap.put("index", toModuloIndex(axes)); variableMap.put("uniqueId", toIndex(axes)); result = Hudson.getInstance().getScriptSupport().evaluateExpression(expression, variableMap); } return TRUE.equals(result); } public int compareTo(Combination that) { int d = this.size() - that.size(); if (d != 0) { return d; } Iterator> itr = this.entrySet().iterator(); Iterator> jtr = that.entrySet().iterator(); while (itr.hasNext()) { Map.Entry i = itr.next(); Map.Entry j = jtr.next(); d = i.getKey().compareTo(j.getKey()); if (d != 0) { return d; } d = i.getValue().compareTo(j.getValue()); if (d != 0) { return d; } } return 0; } /** * Works like {@link #toString()} but only include the given axes. */ public String toString(Collection subset) { if (size() == 1 && subset.size() == 1) { return values().iterator().next(); } StringBuilder buf = new StringBuilder(); for (Axis a : subset) { if (buf.length() > 0) { buf.append(','); } buf.append(a.getName()).append('=').append(get(a)); } if (buf.length() == 0) { buf.append("default"); // special case to avoid 0-length name. } return buf.toString(); } /** * Gets the values that correspond to the specified axes, in their order. */ public List values(Collection axes) { List r = new ArrayList(axes.size()); for (Axis a : axes) { r.add(get(a)); } return r; } /** * Converts to the ID string representation: * axisName=value,axisName=value,... * * @param sep1 The separator between multiple axes. * @param sep2 The separator between axis name and value. */ public String toString(char sep1, char sep2) { return toString(sep1, sep2, false); } /** * Converts to the ID string representation: * axisName=value,axisName=value,... * * @param sep1 The separator between multiple axes. * @param sep2 The separator between axis name and value. * @param encodeValue true to encode value {@link Util#rawEncode(String)} * @return string representation. */ public String toString(char sep1, char sep2, boolean encodeValue) { StringBuilder builder = new StringBuilder(); if (encodeValue) { for (Map.Entry e : entrySet()) { if (builder.length() > 0) { builder.append(sep1); } builder.append(e.getKey()).append(sep2).append(Util.rawEncode(e.getValue())); } } else { for (Map.Entry e : entrySet()) { if (builder.length() > 0) { builder.append(sep1); } builder.append(e.getKey()).append(sep2).append(e.getValue()); } } if (builder.length() == 0) { builder.append("default"); // special case to avoid 0-length name. } return builder.toString(); } @Override public String toString() { return toString(',', '='); } /** * Gets the 8 character-wide hash code for this combination */ public String digest() { return Util.getDigestOf(toString()); } /** * Reverse operation of {@link #toString()}. */ public static Combination fromString(String id) { if (id.equals("default")) { return new Combination(Collections.emptyMap()); } Map m = new HashMap(); StringTokenizer tokens = new StringTokenizer(id, DELIM); while (tokens.hasMoreTokens()) { String token = tokens.nextToken(); int idx = token.indexOf('='); if (idx < 0) { throw new IllegalArgumentException("Can't parse " + id); } m.put(token.substring(0, idx), token.substring(idx + 1)); } return new Combination(m); } /** * Creates compact string representataion suitable for display purpose. * *

The string is made compact by looking for {@link Axis} whose values * are unique, and omit the axis name. */ public String toCompactString(AxisList axes) { Set nonUniqueAxes = new HashSet(); Map axisByValue = new HashMap(); for (Axis a : axes) { for (String v : a.getValues()) { Axis old = axisByValue.put(v, a); if (old != null) { // these two axes have colliding values nonUniqueAxes.add(old.getName()); nonUniqueAxes.add(a.getName()); } } } StringBuilder buf = new StringBuilder(); for (Map.Entry e : entrySet()) { if (buf.length() > 0) { buf.append(','); } if (nonUniqueAxes.contains(e.getKey())) { buf.append(e.getKey()).append('='); } buf.append(e.getValue()); } if (buf.length() == 0) { buf.append("default"); // special case to avoid 0-length name. } return buf.toString(); } // read-only @Override public void clear() { throw new UnsupportedOperationException(); } @Override public void putAll(Map map) { throw new UnsupportedOperationException(); } @Override public String put(String key, String value) { throw new UnsupportedOperationException(); } @Override public String remove(Object key) { throw new UnsupportedOperationException(); } /** * Duck-typing for boolean expressions. * * @see Combination#evalScriptExpression(AxisList,String) */ public static final class BooleanCategory { /** * x -> y */ public static Boolean implies(Boolean lhs, Boolean rhs) { return !lhs || rhs; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy