/*
 * Bytecode Analysis Framework
 * Copyright (C) 2003,2004 University of Maryland
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package edu.umd.cs.findbugs.ba.vna;

import edu.umd.cs.findbugs.util.MapCache;
import edu.umd.cs.findbugs.util.Util;

/**
 * A "value number" is a value produced somewhere in a methods. We use value
 * numbers as dataflow values in Frames. When two frame slots have the same
 * value number, then the same value is in both of those slots.
 * <p/>
 * <p>
 * Instances of ValueNumbers produced by the same {@link ValueNumberFactory
 * ValueNumberFactory} are unique, so reference equality may be used to
 * determine whether or not two value numbers are the same. In general,
 * ValueNumbers from different factories cannot be compared.
 * 
 * @author David Hovemeyer
 * @see ValueNumberAnalysis
 */
public class ValueNumber implements Comparable<ValueNumber> {
    static MapCache<ValueNumber, ValueNumber> cache = new MapCache<ValueNumber, ValueNumber>(200);

    static int valueNumbersCreated = 0;

    static int valueNumbersReused = 0;
    
    public static int mergeFlags(int flags1, int flags2) {
        if (flags1 == -1) return flags2;
        if (flags2 == -1) return flags1;
        return flags1 & flags2;
    }

    public static synchronized ValueNumber createValueNumber(int number, int flags) {
        ValueNumber probe = new ValueNumber(number, flags);
        ValueNumber result = cache.get(probe);
        if (result != null) {
            valueNumbersReused++;
            return result;
        }
        cache.put(probe, probe);
        valueNumbersCreated++;
        return probe;
    }

    public static ValueNumber createValueNumber(int number) {
        return createValueNumber(number, 0);
    }

    static {
        Util.runLogAtShutdown(new Runnable() {

            public void run() {
                System.out.println("Value number statistics: " + valueNumbersCreated + " created, " + valueNumbersReused
                        + " reused");

            }
        });
    }

    /**
     * The value number.
     */
    final int number;

    /**
     * Flags representing meta information about the value. When value numbers are merged, 
     * their flags should be the flags common to both.
     */
    final int flags;

    /**
     * Flag specifying that this value was the return value of a called method.
     */
    public static final int RETURN_VALUE = 1;

    public static final int ARRAY_VALUE = 2;

    public static final int CONSTANT_CLASS_OBJECT = 4;

    public static final int PHI_NODE = 8;

    public static final int CONSTANT_VALUE = 16;

    /**
     * Constructor.
     * 
     * @param number
     *            the value number
     */
    private ValueNumber(int number) {
        this.number = number;
        this.flags = 0;
    }

    private ValueNumber(int number, int flags) {
        this.number = number;
        this.flags = flags;
    }

    public int getNumber() {
        return number;
    }

    public int getFlags() {
        return flags;
    }

    public boolean hasFlag(int flag) {
        return (flags & flag) == flag;
    }

    @Override
    public String toString() {
        if (flags != 0)
            return number + "(" + flags + "),";
        return number + ",";
    }

    @Override
    public int hashCode() {
        return number * 17 + flags;
    }

    @Override
    public boolean equals(Object o) {
        if (o instanceof ValueNumber) {
            return number == ((ValueNumber) o).number && flags == ((ValueNumber) o).flags;
        }
        return false;
    }

    public int compareTo(ValueNumber other) {
        int result = number - other.number;
        if (result != 0)
            return result;
        return flags - other.flags;

    }
}

// vim:ts=4
