/* * Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of Business Objects nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * TypeColourManager.java * Created: Nov 29, 2000 * By: Luke Evans */ package org.openquark.gems.client; import java.awt.Color; import org.openquark.cal.compiler.TypeConsApp; import org.openquark.cal.compiler.TypeExpr; import org.openquark.cal.module.Cal.Core.CAL_Prelude; /** * The TypeColourManager keeps track on colour mappings to the unique types * which are currently in use on the TableTop. * The goal of the TypeColourManager is to distribute unique types * uniformly across the colour space, so as to differentiate them. * * When types are looked up for which there is no current mapping, a new * colour mapping is created for the type. * * Creation date: (11/29/00 9:24:54 AM) * @author Luke Evans */ public class TypeColourManager implements TypeColourProvider { private static final float PHI = 0.618F; // phi, the golden angle private static final float SAT_DELTA = 0.64F; // How much we move into the middle private final java.util.AbstractMap<String, TypeColour> colourMap; // content addressible map private int count = 0; // Which colour to issue next /** * An array containing a bunch of fairly distinct colours. */ private static final int[] COLOUR_ARRAY = { 0xFF0000, // Red 0x00FF00, // Green 0x0000FF, // Blue 0xFF00FF, // Magenta 0x00FFFF, // Cyan 0xFFFF00, // Yellow 0xFF7F00, // Orange 0xB5A642, // Brass 0x5F9F9F, // Cadet Blue 0xB87333, // Copper 0x38B0DE, // Summer Sky 0x9932CD, // Dark Orchide 0xDB9370, // Tan 0x855E42, // Dark Wood 0xEAADEA, // Plum 0x8E2323, // Firebrick 0xF5CCB0, // Flesh 0x238E23, // Forest Green 0xADEAEA, // Turquoise 0xDBDB70, // Orchid 0xC0C0C0, // Grey 0x527F76, // Green Copper 0x9F9F5F, // Khaki 0x8E236B, // Maroon 0xCC3299, // Violet Red 0xEBC79E, // New Tan 0xCFB53B, // Old Gold 0x70DB93, // Aquamarine 0x236B8E, // Steel Blue 0xD9D9F3, // Quartz 0x5959AB, // Rich Blue 0xE47833, // Mandarin Orange 0x238E68, // Sea Green 0x871F78, // Dark Purple 0x8E6B23, // Sienna 0xFF1CAE, // Spicy Pink }; /** * TypeColour is a single mapping from a type to a colour. * Creation date: (11/29/00 9:35:35 AM) * @author Luke Evans */ static class TypeColour { final String typeString; final Color colour; /** * Construct TypeColour from a type only (colour defaults to black) * @param type the type expression describing the type */ public TypeColour(TypeExpr type) { this(type, Color.black); } /** * Construct TypeColour from a type and a colour * @param type the type expression describing the type * @param colour the colour which represents the type */ public TypeColour(TypeExpr type, Color colour) { this(type.toString(), colour); } TypeColour(String typeString, Color colour) { this.typeString = typeString; this.colour = colour; } /** * Generate a hash code for this TypeColour * Creation date: (11/29/00 9:39:01 AM) * @return the hash code */ @Override public int hashCode() { // The hash code of this TypeColour is the same of the hash code // for its name return typeString.hashCode(); } /** * Describe this TypeColour * Creation date: (11/29/00 7:07:44 PM) * @return the description of the TypeColour mapping */ @Override public String toString() { return typeString + "->" + colour; } /** * Test for equality of this TypeColour with another. * @param obj the other object * @return boolean whether equivalent */ @Override public boolean equals(Object obj) { // Must be type equivalent object and have the same type if (obj instanceof TypeColour) { if (((TypeColour)obj).typeString.equals(typeString)) { // They are the same return true; } } // Not equal return false; } } /** * Default constructor for the TypeColourManager. */ public TypeColourManager() { super(); // Create the actual colour map colourMap = new java.util.HashMap<String, TypeColour>(); // Automatically assign colours to basic Types (this ensures consistent // colours for these types). addTypeString(CAL_Prelude.TypeConstructors.Boolean.getQualifiedName()); addTypeString(CAL_Prelude.TypeConstructors.Char.getQualifiedName()); addTypeString(CAL_Prelude.TypeConstructors.Int.getQualifiedName()); addTypeString(CAL_Prelude.TypeConstructors.Double.getQualifiedName()); addType(TypeExpr.makeParametricType()); addTypeString("[" + CAL_Prelude.TypeConstructors.Boolean.getQualifiedName() + "]"); addTypeString(CAL_Prelude.TypeConstructors.String.getQualifiedName()); addTypeString("[" + CAL_Prelude.TypeConstructors.Char.getQualifiedName() + "]"); addTypeString("[" + CAL_Prelude.TypeConstructors.Int.getQualifiedName() + "]"); addTypeString("[" + CAL_Prelude.TypeConstructors.Double.getQualifiedName() + "]"); addTypeString("[a]"); addTypeString("[" + CAL_Prelude.TypeConstructors.String.getQualifiedName() + "]"); //lists of strings addTypeString("[" + CAL_Prelude.TypeConstructors.Char.getQualifiedName() + "]"); //lists of strings addType(TypeExpr.makeTupleType(2)); // (a, b) addType(TypeExpr.makeTupleType(3)); // (a, b, c) //todoBI fix this up later. //addType (TypeChecker.createNumTypeVar()); // Num a => a } /** * Add a given type to the TypeColourManager. * Note that types are added as needed to the TypeColourManager as their colours are requested in getTypeColour(). * One reason to use this method is to ensure that types are added to the manager in a certain order. * @param type the type to add */ private void addType(TypeExpr type) { if (type == null) { throw new NullPointerException(); } // Check if this type is already present, otherwise add it if (!colourMap.containsKey(type.toString())) { // If it wasn't present, it will need to be allocated a new colour TypeColour newTC = new TypeColour(type, getNextColour()); colourMap.put(type.toString(), newTC); } //add argument types of type constructors TypeConsApp typeConsApp = type.rootTypeConsApp(); if (typeConsApp != null) { for (int i = 0, nArgs = typeConsApp.getNArgs(); i < nArgs; ++i) { addType(typeConsApp.getArg(i)); } } } /** * Adds a given type to the TypeColourManager. Unlike addType, this does not add type arguments, since it * doesn't inspect the type. It is primarily intended for initialization of this class. * @param typeString */ private void addTypeString(String typeString) { if (typeString == null) { throw new NullPointerException(); } // Check if this type is already present, otherwise add it if (!colourMap.containsKey(typeString)) { // If it wasn't present, it will need to be allocated a new colour TypeColour newTC = new TypeColour(typeString, getNextColour()); colourMap.put(typeString, newTC); } } /** * Return the next colour to use for a type. * @return Color the colour to use */ private Color getNextColour() { // Note: The first batch of colours are pre-determined in the COLOUR_ARRAY and are fairly distinguishable from each other. // After that, use the algorithm. if (count >= COLOUR_ARRAY.length) { /* We use an interesting algorithm to define what the next colour is to use: Phi (the golden angle of 137.5 degrees, or 0.618 of a turn) is used to spiral around the HSV colour circle to generate different colours. The Hue is canonically attributed to the angle of the polar coordinate of the colour, and the length of the coordinate is the saturation. The value is always 100% */ // Calculate the hue and saturation float hue = PHI * count; float satFalloff = count * SAT_DELTA - (int) (count * SAT_DELTA); float sat = 1.0F - satFalloff; // Increment the colour count and return the new colour count++; return Color.getHSBColor(hue, sat, 1.0F); } int colourCode = COLOUR_ARRAY[count]; count++; return new Color(colourCode); } /** * Obtain a colour which can represent this type uniquely * @param type the type expression to find a colour for * @return Color the colour to use for this type, or black if the type is null. */ public Color getTypeColour(TypeExpr type) { if (type == null) { // bound inputs and outputs do not have an associated type return Color.black; } // add to the colour mapping if it's not already there addType(type); // Get the matching TypeColour TypeColour thisTC = colourMap.get(type.toString()); // Return the mapped colour or black if it's all gone horribly wrong! return (thisTC != null)? thisTC.colour : Color.black; } }