/**
 * Chart2D, a java library for drawing two dimensional charts.
 * Copyright (C) 2001 Jason J. Simas
 *
 * 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
 *
 * The author of this library may be contacted at:
 * E-mail:  jjsimas@users.sourceforge.net
 * Street Address:  J J Simas, 887 Tico Road, Ojai, CA 93023-3555 USA
 */


package net.sourceforge.chart2d;


import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;


/**
 * A data structure for holding the properties common to all GraphChart2D objects.
 * A GraphChart2D objects has axes and one or more overlaid graphs.
 * Pass this to any number of GraphChart2D objects.
 */
 public final class GraphChart2DProperties extends Properties {


  /**
   * Indicates the ticks of an axis should be centered on each label
   * of the axis.  Used by setLabelsAxisTicksAlignment (int).
   */
  public static final int CENTERED = 0;

  /**
   * Indicates the ticks of an axis should be centered between the labels
   * of the axis.  Used by setLabelsAxisTicksAlignment (int).
   */
  public static final int BETWEEN = 1;

  /**
   * The default is false.
   */
  public static final boolean CHART_DATASET_CUSTOMIZE_GREATEST_VALUE_DEFAULT = false;

  /**
   * The default is 0.
   */
  public static final float CHART_DATASET_CUSTOM_GREATEST_VALUE_DEFAULT = 0;

  /**
   * The default is false.
   */
  public static final boolean CHART_DATASET_CUSTOMIZE_LEAST_VALUE_DEFAULT = false;

  /**
   * The default is 0.
   */
  public static final float CHART_DATASET_CUSTOM_LEAST_VALUE_DEFAULT = 0;

  /**
   * The default is .95f.
   */
  public static final float CHART_GRAPHABLE_TO_AVAILABLE_RATIO_DEFAULT = 1f;

  /**
   * The default is 7.
   */
  public static final int NUMBERS_AXIS_NUM_LABELS_DEFAULT = 7;


  /**
   * The default is true.
   */
  public static final boolean LABELS_AXIS_EXISTENCE_DEFAULT = true;


  /**
   * The default is BETWEEN.
   */
  public static final int LABELS_AXIS_TICKS_ALIGNMENT_DEFAULT = BETWEEN;

  /**
   * The default is String[0].
   */
  public static final String[] LABELS_AXIS_LABELS_TEXTS_DEFAULT = new String[0];

  /**
   * The default is true.
   */
  public static final boolean LABELS_AXIS_TITLE_EXISTENCE_DEFAULT = true;

  /**
   * The default is "".
   */
  public static final String LABELS_AXIS_TITLE_TEXT_DEFAULT = "";

  /**
   * The default is 11.
   */
  public static final int LABELS_AXIS_TITLE_FONT_POINT_MODEL_DEFAULT = 11;

  /**
   * The default is "SansSerif".
   */
  public static final String LABELS_AXIS_TITLE_FONT_NAME_DEFAULT = "SansSerif";

  /**
   * The default is Color.black.
   */
  public static final Color LABELS_AXIS_TITLE_FONT_COLOR_DEFAULT = Color.black;

  /**
   * The default is Font.PLAIN.
   */
  public static final int LABELS_AXIS_TITLE_FONT_STYLE_DEFAULT = Font.PLAIN;

  /**
   * The default is true.
   */
  public static final boolean LABELS_AXIS_TITLE_BETWEEN_REST_GAP_EXISTENCE_DEFAULT = true;

  /**
   * The default is 3.
   */
  public static final int LABELS_AXIS_TITLE_BETWEEN_REST_GAP_THICKNESS_MODEL_DEFAULT = 3;

  /**
   * The default is true.
   */
  public static final boolean LABELS_AXIS_TICKS_EXISTENCE_DEFAULT = true;

  /**
   * The default is Dimension (3, 3).
   */
  public static final Dimension LABELS_AXIS_TICKS_SIZE_MODEL_DEFAULT = new Dimension (3, 3);

  /**
   * The default is Color.black.
   */
  public static final Color LABELS_AXIS_TICKS_COLOR_DEFAULT = Color.black;

  /**
   * The default is false.
   */
  public static final boolean LABELS_AXIS_TICKS_OUTLINE_EXISTENCE_DEFAULT = false;

  /**
   * The default is Color.black.
   */
  public static final Color LABELS_AXIS_TICKS_OUTLINE_COLOR_DEFAULT = Color.black;

  /**
   * The default is 10.
   */
  public static final int LABELS_AXIS_LABELS_FONT_POINT_MODEL_DEFAULT = 10;

  /**
   * The default is "SansSerif".
   */
  public static final String LABELS_AXIS_LABELS_FONT_NAME_DEFAULT = "SansSerif";

  /**
   * The default is Color.black.
   */
  public static final Color LABELS_AXIS_LABELS_FONT_COLOR_DEFAULT = Color.black;

  /**
   * The default is Font.PLAIN.
   */
  public static final int LABELS_AXIS_LABELS_FONT_STYLE_DEFAULT = Font.PLAIN;

  /**
   * The default is true.
   */
  public static final boolean LABELS_AXIS_BETWEEN_LABELS_OR_TICKS_GAP_EXISTENCE_DEFAULT = true;

  /**
   * The default is 3.
   */
  public static final int LABELS_AXIS_BETWEEN_LABELS_OR_TICKS_GAP_THICKNESS_MODEL_DEFAULT = 3;

  /**
   * The default is true.
   */
  public static final boolean LABELS_AXIS_BETWEEN_LABELS_AND_TICKS_GAP_EXISTENCE_DEFAULT = true;

  /**
   * The default is 3.
   */
  public static final int LABELS_AXIS_BETWEEN_LABELS_AND_TICKS_GAP_THICKNESS_MODEL_DEFAULT = 3;

  /**
   * The default is true.
   */
  public static final boolean NUMBERS_AXIS_TITLE_EXISTENCE_DEFAULT = true;

  /**
   * The default is "".
   */
  public static final String NUMBERS_AXIS_TITLE_TEXT_DEFAULT = "";

  /**
   * The default is 11.
   */
  public static final int NUMBERS_AXIS_TITLE_FONT_POINT_MODEL_DEFAULT = 11;

  /**
   * The default is "SansSerif".
   */
  public static final String NUMBERS_AXIS_TITLE_FONT_NAME_DEFAULT = "SansSerif";

  /**
   * The default is Color.black.
   */
  public static final Color NUMBERS_AXIS_TITLE_FONT_COLOR_DEFAULT = Color.black;

  /**
   * The default is Font.PLAIN.
   */
  public static final int NUMBERS_AXIS_TITLE_FONT_STYLE_DEFAULT = Font.PLAIN;

  /**
   * The default is true.
   */
  public static final boolean NUMBERS_AXIS_TITLE_BETWEEN_REST_GAP_EXISTENCE_DEFAULT = true;

  /**
   * The default is 3.
   */
  public static final int NUMBERS_AXIS_TITLE_BETWEEN_REST_GAP_THICKNESS_MODEL_DEFAULT = 3;

  /**
   * The default is true.
   */
  public static final boolean NUMBERS_AXIS_TICKS_EXISTENCE_DEFAULT = true;

  /**
   * The default is Dimension (3, 3).
   */
  public static final Dimension NUMBERS_AXIS_TICKS_SIZE_MODEL_DEFAULT = new Dimension (3, 3);

  /**
   * The default is Color.black.
   */
  public static final Color NUMBERS_AXIS_TICKS_COLOR_DEFAULT = Color.black;

  /**
   * The default is false.
   */
  public static final boolean NUMBERS_AXIS_TICKS_OUTLINE_EXISTENCE_DEFAULT = false;

  /**
   * The default is Color.black.
   */
  public static final Color NUMBERS_AXIS_TICKS_OUTLINE_COLOR_DEFAULT = Color.black;

  /**
   * The default is 10.
   */
  public static final int NUMBERS_AXIS_LABELS_FONT_POINT_MODEL_DEFAULT = 10;

  /**
   * The default is "SansSerif".
   */
  public static final String NUMBERS_AXIS_LABELS_FONT_NAME_DEFAULT = "SansSerif";

  /**
   * The default is Color.black.
   */
  public static final Color NUMBERS_AXIS_LABELS_FONT_COLOR_DEFAULT = Color.black;

  /**
   * The default is Font.PLAIN.
   */
  public static final int NUMBERS_AXIS_LABELS_FONT_STYLE_DEFAULT = Font.PLAIN;

  /**
   * The default is true.
   */
  public static final boolean NUMBERS_AXIS_BETWEEN_LABELS_OR_TICKS_GAP_EXISTENCE_DEFAULT = true;

  /**
   * The default is 3.
   */
  public static final int NUMBERS_AXIS_BETWEEN_LABELS_OR_TICKS_GAP_THICKNESS_MODEL_DEFAULT = 3;

  /**
   * The default is true.
   */
  public static final boolean NUMBERS_AXIS_BETWEEN_LABELS_AND_TICKS_GAP_EXISTENCE_DEFAULT = true;

  /**
   * The default is 3.
   */
  public static final int NUMBERS_AXIS_BETWEEN_LABELS_AND_TICKS_GAP_THICKNESS_MODEL_DEFAULT = 3;

  /**
   * The default is false.
   */
  public static final boolean GRAPH_COMPONENTS_COLORING_BY_CAT_DEFAULT = false;

  /**
   * The default is null.
   */
  public static final MultiColorsProperties GRAPH_COMPONENTS_COLORS_BY_CAT_DEFAULT = null;


  private final class LabelsAxisConfig {

    private boolean existence;
    private int ticksAlignment;
    private String[] labelsTexts;
    private boolean titleExistence;
    private String titleText;
    private int titleFontPointModel;
    private String titleFontName;
    private Color titleFontColor;
    private int titleFontStyle;
    private boolean titleBetweenRestGapExistence;
    private int titleBetweenRestGapThicknessModel;
    private boolean ticksExistence;
    private Dimension ticksSizeModel;
    private Color ticksColor;
    private boolean ticksOutlineExistence;
    private Color ticksOutlineColor;
    private int labelsFontPointModel;
    private String labelsFontName;
    private Color labelsFontColor;
    private int labelsFontStyle;
    private boolean betweenLabelsOrTicksGapExistence;
    private int betweenLabelsOrTicksGapThicknessModel;
    private boolean betweenLabelsAndTicksGapExistence;
    private int betweenLabelsAndTicksGapThicknessModel;

    private void setDefaults() {

      existence = LABELS_AXIS_EXISTENCE_DEFAULT;
      ticksAlignment = LABELS_AXIS_TICKS_ALIGNMENT_DEFAULT;
      labelsTexts = copyLabelsTexts (LABELS_AXIS_LABELS_TEXTS_DEFAULT);
      titleExistence = LABELS_AXIS_TITLE_EXISTENCE_DEFAULT;
      titleText = LABELS_AXIS_TITLE_TEXT_DEFAULT;
      titleFontPointModel = LABELS_AXIS_TITLE_FONT_POINT_MODEL_DEFAULT;
      titleFontName = LABELS_AXIS_TITLE_FONT_NAME_DEFAULT;
      titleFontColor = LABELS_AXIS_TITLE_FONT_COLOR_DEFAULT;
      titleFontStyle = LABELS_AXIS_TITLE_FONT_STYLE_DEFAULT;
      titleBetweenRestGapExistence = LABELS_AXIS_TITLE_BETWEEN_REST_GAP_EXISTENCE_DEFAULT;
      titleBetweenRestGapThicknessModel = LABELS_AXIS_TITLE_BETWEEN_REST_GAP_THICKNESS_MODEL_DEFAULT;
      ticksExistence = LABELS_AXIS_TICKS_EXISTENCE_DEFAULT;
      ticksSizeModel = copyDimension (LABELS_AXIS_TICKS_SIZE_MODEL_DEFAULT);
      ticksColor = LABELS_AXIS_TICKS_COLOR_DEFAULT;
      ticksOutlineExistence = LABELS_AXIS_TICKS_OUTLINE_EXISTENCE_DEFAULT;
      ticksOutlineColor = LABELS_AXIS_TICKS_OUTLINE_COLOR_DEFAULT;
      labelsFontPointModel = LABELS_AXIS_LABELS_FONT_POINT_MODEL_DEFAULT;
      labelsFontName = LABELS_AXIS_LABELS_FONT_NAME_DEFAULT;
      labelsFontColor = LABELS_AXIS_LABELS_FONT_COLOR_DEFAULT;
      labelsFontStyle = LABELS_AXIS_LABELS_FONT_STYLE_DEFAULT;
      betweenLabelsOrTicksGapExistence = LABELS_AXIS_BETWEEN_LABELS_OR_TICKS_GAP_EXISTENCE_DEFAULT;
      betweenLabelsOrTicksGapThicknessModel =
        LABELS_AXIS_BETWEEN_LABELS_OR_TICKS_GAP_THICKNESS_MODEL_DEFAULT;
      betweenLabelsAndTicksGapExistence =
        LABELS_AXIS_BETWEEN_LABELS_AND_TICKS_GAP_EXISTENCE_DEFAULT;
      betweenLabelsAndTicksGapThicknessModel =
        LABELS_AXIS_BETWEEN_LABELS_AND_TICKS_GAP_THICKNESS_MODEL_DEFAULT;
    }

    private void copyFrom (LabelsAxisConfig config) {

      existence = config.existence;
      ticksAlignment = config.ticksAlignment;
      labelsTexts = copyLabelsTexts (config.labelsTexts);
      titleExistence = config.titleExistence;
      titleText = config.titleText;
      titleFontPointModel = config.titleFontPointModel;
      titleFontName = config.titleFontName;
      titleFontColor = config.titleFontColor;
      titleFontStyle = config.titleFontStyle;
      titleBetweenRestGapExistence = config.titleBetweenRestGapExistence;
      titleBetweenRestGapThicknessModel = config.titleBetweenRestGapThicknessModel;
      ticksExistence = config.ticksExistence;
      ticksSizeModel = copyDimension (config.ticksSizeModel);
      ticksColor = config.ticksColor;
      ticksOutlineExistence = config.ticksOutlineExistence;
      ticksOutlineColor = config.ticksOutlineColor;
      labelsFontPointModel = config.labelsFontPointModel;
      labelsFontName = config.labelsFontName;
      labelsFontColor = config.labelsFontColor;
      labelsFontStyle = config.labelsFontStyle;
      betweenLabelsOrTicksGapExistence = config.betweenLabelsOrTicksGapExistence;
      betweenLabelsOrTicksGapThicknessModel = config.betweenLabelsOrTicksGapThicknessModel;
      betweenLabelsAndTicksGapExistence = config.betweenLabelsAndTicksGapExistence;
      betweenLabelsAndTicksGapThicknessModel = config.betweenLabelsAndTicksGapThicknessModel;
    }

    private boolean validate (boolean debug) {

      boolean valid = true;

      if (ticksAlignment != BETWEEN && ticksAlignment != CENTERED) {
        valid = false;
        if (debug) System.out.println ("Problem with LabelsAxisTicksAlignment");
      }
      if (labelsTexts == null) {
        valid = false;
        if (debug) System.out.println ("LabelsAxisLabelsTexts == null");
      }
      if (titleText == null) {
        valid = false;
        if (debug) System.out.println ("LabelsAxisTitleText == null");
      }
      if (titleFontPointModel < 0) {
        valid = false;
        if (debug) System.out.println ("LabelsAxisTitleFontPointModel < 0");
      }
      if (titleFontName == null || !isFontNameExists (titleFontName)) {
        valid = false;
        if (debug) System.out.println ("Problem with LabelsAxisTitleFontName");
      }
      if (titleFontColor == null) {
        valid = false;
        if (debug) System.out.println ("LabelsAxisTitleFontColor == null");
      }
      if (!isFontStyleValid (titleFontStyle)) {
        valid = false;
        if (debug) System.out.println ("Problem with LabelsAxisTitleFontStyle");
      }
      if (titleBetweenRestGapThicknessModel < 0) {
        valid = false;
        if (debug) System.out.println ("LabelsAxisTitleBetweenRestGapThicknessModel < 0");
      }
      if (ticksSizeModel == null || ticksSizeModel.width < 0 || ticksSizeModel.height < 0) {
        valid = false;
        if (debug) System.out.println ("Problem with LabelsAxisTicksSizeModel");
      }
      if (ticksColor == null) {
        valid = false;
        if (debug) System.out.println ("LabelsAxisTicksColor == null");
      }
      if (ticksOutlineColor == null) {
        valid = false;
        if (debug) System.out.println ("LabelsAxisTicksOutlineColor == null");
      }
      if (labelsFontPointModel < 0) {
        valid = false;
        if (debug) System.out.println ("LabelsAxisLabelsFontPointModel < 0");
      }
      if (labelsFontName == null || !isFontNameExists (labelsFontName)) {
        valid = false;
        if (debug) System.out.println ("Problem with LabelsAxisLabelsFontName");
      }
      if (labelsFontColor == null) {
        valid = false;
        if (debug) System.out.println ("LabelsAxisLabelsFontColor == null");
      }
      if (!isFontStyleValid (labelsFontStyle)) {
        valid = false;
        if (debug) System.out.println ("Problem with LabelsAxisLabelsFontStyle");
      }
      if (betweenLabelsOrTicksGapThicknessModel < 0) {
        valid = false;
        if (debug) System.out.println ("LabelsAxisBetweenLabelsOrTicksGapThicknessModel < 0");
      }
      if (betweenLabelsAndTicksGapThicknessModel < 0) {
        valid = false;
        if (debug) System.out.println ("LabelsAxisBetweenLabelsAndTicksGapThicknessModel < 0");
      }

      return valid;
    }
  }


  private final class NumbersAxisConfig {

    private int numLabels;
    private boolean titleExistence;
    private String titleText;
    private int titleFontPointModel;
    private String titleFontName;
    private Color titleFontColor;
    private int titleFontStyle;
    private boolean titleBetweenRestGapExistence;
    private int titleBetweenRestGapThicknessModel;
    private boolean ticksExistence;
    private Dimension ticksSizeModel;
    private Color ticksColor;
    private boolean ticksOutlineExistence;
    private Color ticksOutlineColor;
    private int labelsFontPointModel;
    private String labelsFontName;
    private Color labelsFontColor;
    private int labelsFontStyle;
    private boolean betweenLabelsOrTicksGapExistence;
    private int betweenLabelsOrTicksGapThicknessModel;
    private boolean betweenLabelsAndTicksGapExistence;
    private int betweenLabelsAndTicksGapThicknessModel;

    private void setDefaults() {

      numLabels = NUMBERS_AXIS_NUM_LABELS_DEFAULT;
      titleExistence = NUMBERS_AXIS_TITLE_EXISTENCE_DEFAULT;
      titleText = NUMBERS_AXIS_TITLE_TEXT_DEFAULT;
      titleFontPointModel = NUMBERS_AXIS_TITLE_FONT_POINT_MODEL_DEFAULT;
      titleFontName = NUMBERS_AXIS_TITLE_FONT_NAME_DEFAULT;
      titleFontColor = NUMBERS_AXIS_TITLE_FONT_COLOR_DEFAULT;
      titleFontStyle = NUMBERS_AXIS_TITLE_FONT_STYLE_DEFAULT;
      titleBetweenRestGapExistence = NUMBERS_AXIS_TITLE_BETWEEN_REST_GAP_EXISTENCE_DEFAULT;
      titleBetweenRestGapThicknessModel = NUMBERS_AXIS_TITLE_BETWEEN_REST_GAP_THICKNESS_MODEL_DEFAULT;
      ticksExistence = NUMBERS_AXIS_TICKS_EXISTENCE_DEFAULT;
      ticksSizeModel = copyDimension (NUMBERS_AXIS_TICKS_SIZE_MODEL_DEFAULT);
      ticksColor = NUMBERS_AXIS_TICKS_COLOR_DEFAULT;
      ticksOutlineExistence = NUMBERS_AXIS_TICKS_OUTLINE_EXISTENCE_DEFAULT;
      ticksOutlineColor = NUMBERS_AXIS_TICKS_OUTLINE_COLOR_DEFAULT;
      labelsFontPointModel = NUMBERS_AXIS_LABELS_FONT_POINT_MODEL_DEFAULT;
      labelsFontName = NUMBERS_AXIS_LABELS_FONT_NAME_DEFAULT;
      labelsFontColor = NUMBERS_AXIS_LABELS_FONT_COLOR_DEFAULT;
      labelsFontStyle = NUMBERS_AXIS_LABELS_FONT_STYLE_DEFAULT;
      betweenLabelsOrTicksGapExistence = NUMBERS_AXIS_BETWEEN_LABELS_OR_TICKS_GAP_EXISTENCE_DEFAULT;
      betweenLabelsOrTicksGapThicknessModel =
        NUMBERS_AXIS_BETWEEN_LABELS_OR_TICKS_GAP_THICKNESS_MODEL_DEFAULT;
      betweenLabelsAndTicksGapExistence =
        NUMBERS_AXIS_BETWEEN_LABELS_AND_TICKS_GAP_EXISTENCE_DEFAULT;
      betweenLabelsAndTicksGapThicknessModel =
        NUMBERS_AXIS_BETWEEN_LABELS_AND_TICKS_GAP_THICKNESS_MODEL_DEFAULT;
    }

    private void copyFrom (NumbersAxisConfig config) {

      numLabels = config.numLabels;
      titleExistence = config.titleExistence;
      titleText = config.titleText;
      titleFontPointModel = config.titleFontPointModel;
      titleFontName = config.titleFontName;
      titleFontColor = config.titleFontColor;
      titleFontStyle = config.titleFontStyle;
      titleBetweenRestGapExistence = config.titleBetweenRestGapExistence;
      titleBetweenRestGapThicknessModel = config.titleBetweenRestGapThicknessModel;
      ticksExistence = config.ticksExistence;
      ticksSizeModel = copyDimension (config.ticksSizeModel);
      ticksColor = config.ticksColor;
      ticksOutlineExistence = config.ticksOutlineExistence;
      ticksOutlineColor = config.ticksOutlineColor;
      labelsFontPointModel = config.labelsFontPointModel;
      labelsFontName = config.labelsFontName;
      labelsFontColor = config.labelsFontColor;
      labelsFontStyle = config.labelsFontStyle;
      betweenLabelsOrTicksGapExistence = config.betweenLabelsOrTicksGapExistence;
      betweenLabelsOrTicksGapThicknessModel = config.betweenLabelsOrTicksGapThicknessModel;
      betweenLabelsAndTicksGapExistence = config.betweenLabelsAndTicksGapExistence;
      betweenLabelsAndTicksGapThicknessModel = config.betweenLabelsAndTicksGapThicknessModel;
    }

    private boolean validate (boolean debug) {

      boolean valid = true;

      if (numLabels < 2) {
        valid = false;
        if (debug) System.out.println ("NumbersAxisNumLabels < 2");
      }
      if (titleText == null) {
        valid = false;
        if (debug) System.out.println ("NumbersAxisTitleText == null");
      }
      if (titleFontPointModel < 0) {
        valid = false;
        if (debug) System.out.println ("NumbersAxisTitleFontPointModel < 0");
      }
      if (titleFontName == null || !isFontNameExists (titleFontName)) {
        valid = false;
        if (debug) System.out.println ("Problem with NumbersAxisTitleFontName");
      }
      if (titleFontColor == null) {
        valid = false;
        if (debug) System.out.println ("NumbersAxisTitleFontColor == null");
      }
      if (!isFontStyleValid (titleFontStyle)) {
        valid = false;
        if (debug) System.out.println ("Problem with NumbersAxisTitleFontStyle");
      }
      if (titleBetweenRestGapThicknessModel < 0) {
        valid = false;
        if (debug) System.out.println ("NumbersAxisTitleBetweenRestGapThicknessModel < 0");
      }
      if (ticksSizeModel == null || ticksSizeModel.width < 0 || ticksSizeModel.height < 0) {
        valid = false;
        if (debug) System.out.println ("Problem with NumbersAxisTicksSizeModel");
      }
      if (ticksColor == null) {
        valid = false;
        if (debug) System.out.println ("NumbersAxisTicksColor == null");
      }
      if (ticksOutlineColor == null) {
        valid = false;
        if (debug) System.out.println ("NumbersAxisTicksOutlineColor == null");
      }
      if (labelsFontPointModel < 0) {
        valid = false;
        if (debug) System.out.println ("NumbersAxisLabelsFontPointModel < 0");
      }
      if (labelsFontName == null || !isFontNameExists (labelsFontName)) {
        valid = false;
        if (debug) System.out.println ("Problem with NumbersAxisLabelsFontName");
      }
      if (labelsFontColor == null) {
        valid = false;
        if (debug) System.out.println ("NumbersAxisLabelsFontColor == null");
      }
      if (!isFontStyleValid (labelsFontStyle)) {
        valid = false;
        if (debug) System.out.println ("Problem with NumbersAxisLabelsFontStyle");
      }
      if (betweenLabelsOrTicksGapThicknessModel < 0) {
        valid = false;
        if (debug) System.out.println ("NumbersAxisBetweenLabelsOrTicksGapThicknessModel < 0");
      }
      if (betweenLabelsAndTicksGapThicknessModel < 0) {
        valid = false;
        if (debug) System.out.println ("NumbersAxisBetweenLabelsAndTicksGapThicknessModel < 0");
      }

      return valid;
    }
  }


  private static String[] copyLabelsTexts (String[] texts) {

    if (texts == null) return null;

    String[] copiedTexts = new String[texts.length];
    for (int i = 0; i < texts.length; ++i) copiedTexts[i] = texts[i];
    return copiedTexts;
  }


  private static Dimension copyDimension (Dimension size) {

    if (size == null) return null;
    return new Dimension (size);
  }


  private static boolean isFontStyleValid (int style) {

    return style == Font.PLAIN ||
      style == Font.ITALIC ||
      style == Font.BOLD ||
      style == (Font.ITALIC|Font.BOLD);
  }


  private boolean chartDatasetCustomizeGreatestValue;
  private float chartDatasetCustomGreatestValue;
  private boolean chartDatasetCustomizeLeastValue;
  private float chartDatasetCustomLeastValue;
  private float chartGraphableToAvailableRatio;

  private final LabelsAxisConfig labelsAxis = new LabelsAxisConfig();
  private final NumbersAxisConfig numbersAxis = new NumbersAxisConfig();

  private boolean graphComponentsColoringByCat;
  private MultiColorsProperties graphComponentsColorsByCat;

  private boolean needsUpdate = true;
  private final DependentUpdateTracker graphChart2DTracker =
    new DependentUpdateTracker (5, 5);


  /**
   * Creates a GraphChart2DProperties object with the documented default values.
   */
  public GraphChart2DProperties() {

    needsUpdate = true;
    setGraphChart2DPropertiesToDefaults();
  }


  /**
   * Creates a GraphChart2DProperties object with property values copied from another object.
   * The copying is a deep copy.
   * @param graphChart2DProps The properties to copy.
   */
  public GraphChart2DProperties (GraphChart2DProperties graphChart2DProps) {

    needsUpdate = true;
    setGraphChart2DProperties (graphChart2DProps);
  }


  /**
   * Sets all properties to their default values.
   */
  public final void setGraphChart2DPropertiesToDefaults() {

    needsUpdate = true;
    chartDatasetCustomizeGreatestValue = CHART_DATASET_CUSTOMIZE_GREATEST_VALUE_DEFAULT;
    chartDatasetCustomGreatestValue = CHART_DATASET_CUSTOM_GREATEST_VALUE_DEFAULT;
    chartDatasetCustomizeLeastValue = CHART_DATASET_CUSTOMIZE_LEAST_VALUE_DEFAULT;
    chartDatasetCustomLeastValue = CHART_DATASET_CUSTOM_LEAST_VALUE_DEFAULT;
    chartGraphableToAvailableRatio = CHART_GRAPHABLE_TO_AVAILABLE_RATIO_DEFAULT;
    labelsAxis.setDefaults();
    numbersAxis.setDefaults();
    graphComponentsColoringByCat = GRAPH_COMPONENTS_COLORING_BY_CAT_DEFAULT;
    setGraphComponentsColorsByCat (GRAPH_COMPONENTS_COLORS_BY_CAT_DEFAULT);
  }


  /**
   * Sets all properties to be the values of another GraphChart2DProperties object.
   * The copying is a deep copy.
   * @param graphChart2DProps The GraphChart2DProperties to copy.
   */
  public final void setGraphChart2DProperties (GraphChart2DProperties graphChart2DProps) {

    needsUpdate = true;
    chartDatasetCustomizeGreatestValue = graphChart2DProps.chartDatasetCustomizeGreatestValue;
    chartDatasetCustomGreatestValue = graphChart2DProps.chartDatasetCustomGreatestValue;
    chartDatasetCustomizeLeastValue = graphChart2DProps.chartDatasetCustomizeLeastValue;
    chartDatasetCustomLeastValue = graphChart2DProps.chartDatasetCustomLeastValue;
    chartGraphableToAvailableRatio = graphChart2DProps.chartGraphableToAvailableRatio;
    labelsAxis.copyFrom (graphChart2DProps.labelsAxis);
    numbersAxis.copyFrom (graphChart2DProps.numbersAxis);
    graphComponentsColoringByCat = graphChart2DProps.graphComponentsColoringByCat;
    setGraphComponentsColorsByCat (
      graphChart2DProps.graphComponentsColorsByCat == null ?
      null : new MultiColorsProperties (graphChart2DProps.graphComponentsColorsByCat));
  }


  /**
   * Sets whether the true greatest value in the data sets will be
   * substituted by the custom value.
   * This effects the range of the labels of the numbers axis.
   * @param customize If true, the greatest value of the data will be
   * customized.
   */
  public final void setChartDatasetCustomizeGreatestValue (boolean customize) {

    needsUpdate = true;
    chartDatasetCustomizeGreatestValue = customize;
  }


  /**
   * Sets the custom greatest value of the data sets.  This value must be
   * greater than or equal to the true greatest value of the data sets for it
   * to be used.
   * This effects the scale of the labels of the numbers axis.
   * @param value The custom greatest value of the data sets.
   */
  public final void setChartDatasetCustomGreatestValue (float value) {

    needsUpdate = true;
    chartDatasetCustomGreatestValue = value;
  }


  /**
   * Sets whether the true least value in the data sets will be
   * substituted by the custom value.
   * This effects the range of the labels of the numbers axis.
   * @param customize If true, the least value of the data will be
   * customized.
   */
  public final void setChartDatasetCustomizeLeastValue (boolean customize) {

    needsUpdate = true;
    chartDatasetCustomizeLeastValue = customize;
  }


  /**
   * Sets the custom least value of the data sets.  This value must be
   * less than or equal to the true least value of the data sets for it
   * to be used.
   * This effects the scale of the labels of the numbers axis.
   * @param value The custom least value of the data sets.
   */
  public final void setChartDatasetCustomLeastValue (float value) {

    needsUpdate = true;
    chartDatasetCustomLeastValue = value;
  }


  /**
   * Sets how much of the chart's graph is used by the graph's components.
   * This value must be: 0f <= value <= 1f.
   * This effects the scale of the labels of the numbers axis.
   * @param ratio The ratio of usable to available of the graph.
   */
  public final void setChartGraphableToAvailableRatio (float ratio) {

    needsUpdate = true;
    chartGraphableToAvailableRatio = ratio;
  }


  /**
   * Sets the existence of the labels axis.  If existence is false, then the labels axis won't
   * be painted and the axis' texts won't need to be set.
   * @param existence If true, then exists.
   */
  public final void setLabelsAxisExistence (boolean existence) {

    needsUpdate = true;
    labelsAxis.existence = existence;
  }


  /**
   * Sets the placement of the ticks on the labels axis.  The ticks may
   * be either between the labels axis labels (ie for bar charts) or centered
   * on each labels axis label (ie for other charts).
   * The possible values are BETWEEN and CENTERED.
   * @param alignment The alignment of the labels axis ticks.
   */
  public final void setLabelsAxisTicksAlignment (int alignment) {

    needsUpdate = true;
    labelsAxis.ticksAlignment = alignment;
  }


  /**
   * Sets the text of each labels axis label.  This determines how many
   * categories the data in the data sets are split into.
   * @param texts The texts of the labels axis labels.
   */
  public final void setLabelsAxisLabelsTexts (String[] texts) {

    needsUpdate = true;
    labelsAxis.labelsTexts = texts;
  }


  /**
   * Sets whether the title of the labels axis exists.
   * @param existence If true, the title exists.
   */
  public final void setLabelsAxisTitleExistence (boolean existence) {

    needsUpdate = true;
    labelsAxis.titleExistence = existence;
  }


  /**
   * Sets the text of the title of the labels axis.
   * @param text The title of the labels axis.
   */
  public final void setLabelsAxisTitleText (String text) {

    needsUpdate = true;
    labelsAxis.titleText = text;
  }


  /**
   * Sets the font point of the labels axis title for the chart's model
   * size.
   * @param point The model font point of the labels axis title.
   */
  public final void setLabelsAxisTitleFontPointModel (int point) {

    needsUpdate = true;
    labelsAxis.titleFontPointModel = point;
  }


  /**
   * Sets the name of the font of the labels axis title.
   * Accepts standard font names.
   * @param name The name of the font of the labels axis title.
   */
  public final void setLabelsAxisTitleFontName (String name) {

    needsUpdate = true;
    labelsAxis.titleFontName = name;
  }


  /**
   * Sets the color of the font of the labels axis title.
   * @param color The color of the font of the labels axis title.
   */
  public final void setLabelsAxisTitleFontColor (Color color) {

    needsUpdate = true;
    labelsAxis.titleFontColor = color;
  }


  /**
   * Sets the style of teh font of the labels axis title.
   * Accepts standard values for font styles.
   * @param style The style of the font of the labels axis title.
   */
  public final void setLabelsAxisTitleFontStyle (int style) {

    needsUpdate = true;
    labelsAxis.titleFontStyle = style;
  }


  /**
   * Sets whether the gap above the labels axis title exists.
   * @param existence If true, the gap exists.
   */
  public final void setLabelsAxisTitleBetweenRestGapExistence (boolean existence) {

    needsUpdate = true;
    labelsAxis.titleBetweenRestGapExistence = existence;
  }


  /**
   * Sets the thickness of the gap above the labels axis title for the
   * chart's model size.
   * @param thickness The model thickness of the gap.
   */
  public final void setLabelsAxisTitleBetweenRestGapThicknessModel (int thickness) {

    needsUpdate = true;
    labelsAxis.titleBetweenRestGapThicknessModel = thickness;
  }


  /**
   * Sets whether there exists ticks along the labels axis.
   * @param existence If true, then they exist.
   */
  public final void setLabelsAxisTicksExistence (boolean existence) {

    needsUpdate = true;
    labelsAxis.ticksExistence = existence;
  }


  /**
   * Sets the size of the labels axis ticks for the chart's model size.
   * @param size The model size of the labels axis ticks.
   */
  public final void setLabelsAxisTicksSizeModel (Dimension size) {

    needsUpdate = true;
    labelsAxis.ticksSizeModel = size;
  }


  /**
   * Sets the color of the labels axis ticks.
   * @param color The color of the labels axis ticks.
   */
  public final void setLabelsAxisTicksColor (Color color) {

    needsUpdate = true;
    labelsAxis.ticksColor = color;
  }


  /**
   * Sets whether a thin line outlines the labels axis ticks.
   * @param existence If true, then a thin outline exists.
   */
  public final void setLabelsAxisTicksOutlineExistence (boolean existence) {

    needsUpdate = true;
    labelsAxis.ticksOutlineExistence = existence;
  }


  /**
   * Sets the color of the thin line that outlines the labels axis ticks.
   * @param color The color of the line that outlines the labels axis ticks.
   */
  public final void setLabelsAxisTicksOutlineColor (Color color) {

    needsUpdate = true;
    labelsAxis.ticksOutlineColor = color;
  }


  /**
   * Sets the point of the font of the labels axis labels for the chart's
   * model size.
   * @param point The model font point of the labels axis labels.
   */
  public final void setLabelsAxisLabelsFontPointModel (int point) {

    needsUpdate = true;
    labelsAxis.labelsFontPointModel = point;
  }


  /**
   * Sets the name of the font of the labels of the labels axis.
   * Accepts standard font names.
   * @param name The name of the font.
   */
  public final void setLabelsAxisLabelsFontName (String name) {

    needsUpdate = true;
    labelsAxis.labelsFontName = name;
  }


  /**
   * Sets the color of the font of the labels of the labels axis.
   * @param color The color of the font.
   */
  public final void setLabelsAxisLabelsFontColor (Color color) {

    needsUpdate = true;
    labelsAxis.labelsFontColor = color;
  }


  /**
   * Sets the style of the font of the labels axis labels.
   * Accepts standard font styles.
   * @param style The style of the font.
   */
  public final void setLabelsAxisLabelsFontStyle (int style) {

    needsUpdate = true;
    labelsAxis.labelsFontStyle = style;
  }


  /**
   * Sets whether a gap between the labels or ticks exists, across.
   * The gap will be applied to whichever are naturally closer.
   * @param existence If true, the gap exists.
   */
  public final void setLabelsAxisBetweenLabelsOrTicksGapExistence (boolean existence) {

    needsUpdate = true;
    labelsAxis.betweenLabelsOrTicksGapExistence = existence;
  }


  /**
   * Sets the thickness of the gap between the labels or ticks,
   * across, for the chart's model size.  The gap will be applied to
   * whichever are naturally closer.
   * @param thicknss The model thickness of the gap.
   */
  public final void setLabelsAxisBetweenLabelsOrTicksGapThicknessModel (int thickness) {

    needsUpdate = true;
    labelsAxis.betweenLabelsOrTicksGapThicknessModel = thickness;
  }


  /**
   * Sets whether a gap between the labels and ticks exists, between.
   * @param existence If true, the gap exists.
   */
  public final void setLabelsAxisBetweenLabelsAndTicksGapExistence (boolean existence) {

    needsUpdate = true;
    labelsAxis.betweenLabelsAndTicksGapExistence = existence;
  }


  /**
   * Sets the thickness of the gap between the labels and ticks,
   * between, for the chart's model size.
   * @param thickness The model thickness of the gap.
   */
  public final void setLabelsAxisBetweenLabelsAndTicksGapThicknessModel (int thickness) {

    needsUpdate = true;
    labelsAxis.betweenLabelsAndTicksGapThicknessModel = thickness;
  }


  /**
   * Sets whether the title of the numbers axis exists.
   * @param existence If true, the title exists.
   */
  public final void setNumbersAxisTitleExistence (boolean existence) {

    needsUpdate = true;
    numbersAxis.titleExistence = existence;
  }


  /**
   * Sets the text of the title of the numbers axis.
   * @param text The title of the numbers axis.
   */
  public final void setNumbersAxisTitleText (String text) {

    needsUpdate = true;
    numbersAxis.titleText = text;
  }


  /**
   * Sets the font point of the numbers axis title for the chart's model
   * size.
   * @param point The model font point of the numbers axis title.
   */
  public final void setNumbersAxisTitleFontPointModel (int point) {

    needsUpdate = true;
    numbersAxis.titleFontPointModel = point;
  }


  /**
   * Sets the name of the font of the numbers axis title.
   * Accepts standard font names.
   * @param name The name of the font of the numbers axis title.
   */
  public final void setNumbersAxisTitleFontName (String name) {

    needsUpdate = true;
    numbersAxis.titleFontName = name;
  }


  /**
   * Sets the color of the font of the numbers axis title.
   * @param color The color of the font of the numbers axis title.
   */
  public final void setNumbersAxisTitleFontColor (Color color) {

    needsUpdate = true;
    numbersAxis.titleFontColor = color;
  }


  /**
   * Sets the style of teh font of the numbers axis title.
   * Accepts standard values for font styles.
   * @param style The style of the font of the numbers axis title.
   */
  public final void setNumbersAxisTitleFontStyle (int style) {

    needsUpdate = true;
    numbersAxis.titleFontStyle = style;
  }


  /**
   * Sets whether the gap right of the numbers axis title exists.
   * @param existence If true, the gap exists.
   */
  public final void setNumbersAxisTitleBetweenRestGapExistence (boolean existence) {

    needsUpdate = true;
    numbersAxis.titleBetweenRestGapExistence = existence;
  }


  /**
   * Sets the thickness of the gap right of the numbers axis title for the
   * chart's model size.
   * @param thickness The model thickness of the gap.
   */
  public final void setNumbersAxisTitleBetweenRestGapThicknessModel (int thickness) {

    needsUpdate = true;
    numbersAxis.titleBetweenRestGapThicknessModel = thickness;
  }


  /**
   * Sets whether there exists ticks along the numbers axis.
   * @param existence If true, then they exist.
   */
  public final void setNumbersAxisTicksExistence (boolean existence) {

    needsUpdate = true;
    numbersAxis.ticksExistence = existence;
  }


  /**
   * Sets the size of the numbers axis ticks for the chart's model size.
   * @param size The model size of the numbers axis ticks.
   */
  public final void setNumbersAxisTicksSizeModel (Dimension size) {

    needsUpdate = true;
    numbersAxis.ticksSizeModel = size;
  }


  /**
   * Sets the color of the numbers axis ticks.
   * @param color The color of the numbers axis ticks.
   */
  public final void setNumbersAxisTicksColor (Color color) {

    needsUpdate = true;
    numbersAxis.ticksColor = color;
  }


  /**
   * Sets whether a thin line outlines the numbers axis ticks.
   * @param existence If true, then a thin outline exists.
   */
  public final void setNumbersAxisTicksOutlineExistence (boolean existence) {

    needsUpdate = true;
    numbersAxis.ticksOutlineExistence = existence;
  }


  /**
   * Sets the color of the thin line that outlines the numbers axis ticks.
   * @param color The color of the line that outliens the numbers axis ticks.
   */
  public final void setNumbersAxisTicksOutlineColor (Color color) {

    needsUpdate = true;
    numbersAxis.ticksOutlineColor = color;
  }


  /**
   * Sets the number of labels in the numbers axis.
   * @param num The number of labels in the numbers axis.
   */
  public final void setNumbersAxisNumLabels (int num) {

    needsUpdate = true;
    numbersAxis.numLabels = num;
  }


  /**
   * Sets the point of the font of the numbers axis labels for the chart's
   * model size.
   * @param point The model font point of the numbers axis labels.
   */
  public final void setNumbersAxisLabelsFontPointModel (int point) {

    needsUpdate = true;
    numbersAxis.labelsFontPointModel = point;
  }


  /**
   * Sets the name of the font of the labels of the numbers axis.
   * Accepts standard font names.
   * @param name The name of the font.
   */
  public final void setNumbersAxisLabelsFontName (String name) {

    needsUpdate = true;
    numbersAxis.labelsFontName = name;
  }


  /**
   * Sets the color of the font of the labels of the numbers axis.
   * @param color The color of the font.
   */
  public final void setNumbersAxisLabelsFontColor (Color color) {

    needsUpdate = true;
    numbersAxis.labelsFontColor = color;
  }


  /**
   * Sets the style of the font of the numbers axis labels.
   * Accepts standard font styles.
   * @param style The style of the font.
   */
  public final void setNumbersAxisLabelsFontStyle (int style) {

    needsUpdate = true;
    numbersAxis.labelsFontStyle = style;
  }


  /**
   * Sets whether a gap between the labels or ticks exists, vertically.
   * The gap will be applied to whichever are naturally closer.
   * @param existence If true, the gap exists.
   */
  public final void setNumbersAxisBetweenLabelsOrTicksGapExistence (boolean existence) {

    needsUpdate = true;
    numbersAxis.betweenLabelsOrTicksGapExistence = existence;
  }


  /**
   * Sets the thickness of the gap between the labels or ticks,
   * vertically, for the chart's model size.  The gap will be applied to
   * whichever are naturally closer.
   * @param thicknss The model thickness of the gap.
   */
  public final void setNumbersAxisBetweenLabelsOrTicksGapThicknessModel (int thickness) {

    needsUpdate = true;
    numbersAxis.betweenLabelsOrTicksGapThicknessModel = thickness;
  }


  /**
   * Sets whether a gap between the labels and ticks exists,
   * horizontally.
   * @param existence If true, the gap exists.
   */
  public final void setNumbersAxisBetweenLabelsAndTicksGapExistence (boolean existence) {

    needsUpdate = true;
    numbersAxis.betweenLabelsAndTicksGapExistence = existence;
  }


  /**
   * Sets the thickness of the gap between the labels and ticks,
   * horizontally, for the chart's model size.
   * @param thickness The model thickness of the gap.
   */
  public final void setNumbersAxisBetweenLabelsAndTicksGapThicknessModel (int thickness) {

    needsUpdate = true;
    numbersAxis.betweenLabelsAndTicksGapThicknessModel = thickness;
  }


  /**
   * Sets whether the graph components have different colors across sets (or across cats).
   * @param b If true, then colors across sets.
   */
  public final void setGraphComponentsColoringByCat (boolean b) {

    needsUpdate = true;
    graphComponentsColoringByCat = b;
  }


  /**
   * Sets the color properties for the colors by cat coloring.
   * @param props The properties of the colors by cat coloring.
   */
  public final void setGraphComponentsColorsByCat (MultiColorsProperties props) {

    needsUpdate = true;
    for (int i = 0; i < graphChart2DTracker.size(); ++i) {
      GraphChart2D graphChart2D =
        (GraphChart2D)graphChart2DTracker.getDependent (i);

      if (graphComponentsColorsByCat != null) {
        graphComponentsColorsByCat.removeObject2D (graphChart2D);
      }
      if (props != null) props.addObject2D (graphChart2D);
    }
    graphComponentsColorsByCat = props;
  }


  /**
   * Gets whether the true greatest value in the data sets will be
   * substituted by the custom value.
   * This effects the range of the labels of the numbers axis.
   * @return boolean If true, the greatest value of the data will be
   * customized.
   */
  public final boolean getChartDatasetCustomizeGreatestValue() {
    return chartDatasetCustomizeGreatestValue;
  }


  /**
   * Gets the custom greatest value of the data sets.  This value must be
   * greater than or equal to the true greatest value of the data sets for it
   * to be used.
   * This effects the scale of the labels of the numbers axis.
   * @return float The custom greatest value of the data sets.
   */
  public final float getChartDatasetCustomGreatestValue() {
    return chartDatasetCustomGreatestValue;
  }


  /**
   * Gets whether the true least value in the data sets will be
   * substituted by the custom value.
   * This effects the range of the labels of the numbers axis.
   * @return boolean If true, the least value of the data will be
   * customized.
   */
  public final boolean getChartDatasetCustomizeLeastValue() {
    return chartDatasetCustomizeLeastValue;
  }


  /**
   * Gets the custom least value of the data sets.  This value must be
   * less than or equal to the true least value of the data sets for it
   * to be used.
   * This effects the scale of the labels of the numbers axis.
   * @return float The custom least value of the data sets.
   */
  public final float getChartDatasetCustomLeastValue() {
    return chartDatasetCustomLeastValue;
  }


  /**
   * Gets how much of the chart's graph is used by the graph's components.
   * This value must be: 0f <= value <= 1f.
   * This effects the scale of the labels of the numbers axis.
   * @return float The ratio of usable to available of the graph.
   */
  public float getChartGraphableToAvailableRatio() {
    return chartGraphableToAvailableRatio;
  }


  /**
   * Gets the existence of the labels axis.  If existence is false, then the labels axis won't
   * be painted and the axis' texts won't need to be set.
   * @return If true, then exists.
   */
  public final boolean getLabelsAxisExistence() {
    return labelsAxis.existence;
  }


  /**
   * Gets the placement of the ticks on the labels axis.  The ticks may
   * be either between the labels axis labels (ie for bar charts) or centered
   * on each labels axis label (ie for other charts).
   * The possible values are BETWEEN and CENTERED.
   * @return int The alignment of the labels axis ticks.
   */
  public final int getLabelsAxisTicksAlignment() {
    return labelsAxis.ticksAlignment;
  }


  /**
   * Gets the text of each labels axis label.  This determines how many
   * categories the data in the data sets are split into.
   * @return String[] The texts of the labels axis labels.
   */
  public final String[] getLabelsAxisLabelsTexts() {
    return labelsAxis.labelsTexts;
  }


  /**
   * Gets whether the title of the labels axis exists.
   * @return boolean If true, the title exists.
   */
  public final boolean getLabelsAxisTitleExistence() {
    return labelsAxis.titleExistence;
  }


  /**
   * Gets the text of the title of the labels axis.
   * @return String The title of the labels axis.
   */
  public final String getLabelsAxisTitleText() {
    return labelsAxis.titleText;
  }


  /**
   * Gets the font point of the labels axis title for the chart's model size.
   * @return int The model font point of the labels axis title.
   */
  public final int getLabelsAxisTitleFontPointModel() {
    return labelsAxis.titleFontPointModel;
  }


  /**
   * Gets the name of the font of the labels axis title.
   * Accepts standard font names.
   * @return String The name of the font of the labels axis title.
   */
  public final String getLabelsAxisTitleFontName() {
    return labelsAxis.titleFontName;
  }


  /**
   * Gets the color of the font of the labels axis title.
   * @return Color The color of the font of the labels axis title.
   */
  public final Color getLabelsAxisTitleFontColor() {
    return labelsAxis.titleFontColor;
  }


  /**
   * Gets the style of teh font of the labels axis title.
   * Accepts standard values for font styles.
   * @return int The style of the font of the labels axis title.
   */
  public final int getLabelsAxisTitleFontStyle() {
    return labelsAxis.titleFontStyle;
  }


  /**
   * Gets whether the gap above the labels axis title exists.
   * @return boolean If true, the gap exists.
   */
  public final boolean getLabelsAxisTitleBetweenRestGapExistence() {
    return labelsAxis.titleBetweenRestGapExistence;
  }


  /**
   * Gets the thickness of the gap above the labels axis title for the
   * chart's model size.
   * @return int The model thickness of the gap.
   */
  public final int getLabelsAxisTitleBetweenRestGapThicknessModel() {
    return labelsAxis.titleBetweenRestGapThicknessModel;
  }


  /**
   * Gets whether there exists ticks along the numbers axis.
   * @return If true, then they exist.
   */
  public final boolean getNumbersAxisTicksExistence() {
    return numbersAxis.ticksExistence;
  }


  /**
   * Gets whether there exists ticks along the labels axis.
   * @return If true, then they exist.
   */
  public final boolean getLabelsAxisTicksExistence() {
    return labelsAxis.ticksExistence;
  }


  /**
   * Gets the size of the labels axis ticks for the chart's model size.
   * @return Dimension The model size of the labels axis ticks.
   */
  public final Dimension getLabelsAxisTicksSizeModel() {
    return labelsAxis.ticksSizeModel;
  }


  /**
   * Gets the color of the labels axis ticks.
   * @return Color The color of the labels axis ticks.
   */
  public final Color getLabelsAxisTicksColor() {
    return labelsAxis.ticksColor;
  }


  /**
   * Gets whether a thin line outlines the labels axis ticks.
   * @return boolean If true, then a thin outline exists.
   */
  public final boolean getLabelsAxisTicksOutlineExistence() {
    return labelsAxis.ticksOutlineExistence;
  }


  /**
   * Gets the color of the thin line that outlines the labels axis ticks.
   * @return Color The color of the line that outliens the labels axis ticks.
   */
  public final Color getLabelsAxisTicksOutlineColor() {
    return labelsAxis.ticksOutlineColor;
  }


  /**
   * Gets the point of the font of the labels axis labels for the chart's
   * model size.
   * @return int The model font point of the labels axis labels.
   */
  public final int getLabelsAxisLabelsFontPointModel() {
    return labelsAxis.labelsFontPointModel;
  }


  /**
   * Gets the name of the font of the labels of the labels axis.
   * Accepts standard font names.
   * @return String The name of the font.
   */
  public final String getLabelsAxisLabelsFontName() {
    return labelsAxis.labelsFontName;
  }


  /**
   * Gets the color of the font of the labels of the labels axis.
   * @return Color The color of the font.
   */
  public final Color getLabelsAxisLabelsFontColor() {
    return labelsAxis.labelsFontColor;
  }


  /**
   * Gets the style of the font of the labels axis labels.
   * Accepts standard font styles.
   * @return int The style of the font.
   */
  public final int getLabelsAxisLabelsFontStyle() {
    return labelsAxis.labelsFontStyle;
  }


  /**
   * Gets whether a gap between the labels or ticks exists, horizontally.
   * The gap will be applied to whichever are naturally closer.
   * @return boolean If true, the gap exists.
   */
  public final boolean getLabelsAxisBetweenLabelsOrTicksGapExistence() {
    return labelsAxis.betweenLabelsOrTicksGapExistence;
  }


  /**
   * Gets the thickness of the gap between the labels or ticks,
   * horizontally, for the chart's model size.  The gap will be applied to
   * whichever are naturally closer.
   * @return int The model thickness of the gap.
   */
  public final int getLabelsAxisBetweenLabelsOrTicksGapThicknessModel() {
    return labelsAxis.betweenLabelsOrTicksGapThicknessModel;
  }


  /**
   * Gets whether a gap between the labels and ticks exists, vertically.
   * @return boolean If true, the gap exists.
   */
  public final boolean getLabelsAxisBetweenLabelsAndTicksGapExistence() {
    return labelsAxis.betweenLabelsAndTicksGapExistence;
  }


  /**
   * Gets the thickness of the gap between the labels and ticks,
   * vertically, for the chart's model size.
   * @return int The model thickness of the gap.
   */
  public final int getLabelsAxisBetweenLabelsAndTicksGapThicknessModel() {
    return labelsAxis.betweenLabelsAndTicksGapThicknessModel;
  }


  /**
   * Gets whether the title of the numbers axis exists.
   * @return boolean If true, the title exists.
   */
  public final boolean getNumbersAxisTitleExistence() {
    return numbersAxis.titleExistence;
  }


  /**
   * Gets the text of the title of the numbers axis.
   * @return String The title of the numbers axis.
   */
  public final String getNumbersAxisTitleText() {
    return numbersAxis.titleText;
  }


  /**
   * Gets the font point of the numbers axis title for the chart's model
   * size.
   * @return int The model font point of the numbers axis title.
   */
  public final int getNumbersAxisTitleFontPointModel() {
    return numbersAxis.titleFontPointModel;
  }


  /**
   * Gets the name of the font of the numbers axis title.
   * Accepts standard font names.
   * @return String The name of the font of the numbers axis title.
   */
  public final String getNumbersAxisTitleFontName() {
    return numbersAxis.titleFontName;
  }


  /**
   * Gets the color of the font of the numbers axis title.
   * @return Color The color of the font of the numbers axis title.
   */
  public final Color getNumbersAxisTitleFontColor() {
    return numbersAxis.titleFontColor;
  }


  /**
   * Gets the style of teh font of the numbers axis title.
   * Accepts standard values for font styles.
   * @return int The style of the font of the numbers axis title.
   */
  public final int getNumbersAxisTitleFontStyle() {
    return numbersAxis.titleFontStyle;
  }


  /**
   * Gets whether the gap right of the numbers axis title exists.
   * @return boolean If true, the gap exists.
   */
  public final boolean getNumbersAxisTitleBetweenRestGapExistence() {
    return numbersAxis.titleBetweenRestGapExistence;
  }


  /**
   * Gets the thickness of the gap right of the numbers axis title for the
   * chart's model size.
   * @return int The model thickness of the gap.
   */
  public final int getNumbersAxisTitleBetweenRestGapThicknessModel() {
    return numbersAxis.titleBetweenRestGapThicknessModel;
  }


  /**
   * Gets the size of the numbers axis ticks for the chart's model size.
   * @return Dimension The model size of the numbers axis ticks.
   */
  public final Dimension getNumbersAxisTicksSizeModel() {
    return numbersAxis.ticksSizeModel;
  }


  /**
   * Gets the color of the numbers axis ticks.
   * @return Color The color of the numbers axis ticks.
   */
  public final Color getNumbersAxisTicksColor() {
    return numbersAxis.ticksColor;
  }


  /**
   * Gets whether a thin line outlines the numbers axis ticks.
   * @return boolean If true, then a thin outline exists.
   */
  public final boolean getNumbersAxisTicksOutlineExistence() {
    return numbersAxis.ticksOutlineExistence;
  }


  /**
   * Gets the color of the thin line that outlines the numbers axis ticks.
   * @return Color The color of the thin line that outliens the numbers axis
   * ticks.
   */
  public final Color getNumbersAxisTicksOutlineColor() {
    return numbersAxis.ticksOutlineColor;
  }


  /**
   * Gets the number of labels in the numbers axis.
   * @return int The number of labels in the numbers axis.
   */
  public final int getNumbersAxisNumLabels() {
    return numbersAxis.numLabels;
  }


  /**
   * Gets the point of the font of the numbers axis labels for the chart's
   * model size.
   * @return int The model font point of the numbers axis labels.
   */
  public final int getNumbersAxisLabelsFontPointModel() {
    return numbersAxis.labelsFontPointModel;
  }


  /**
   * Gets the name of the font of the labels of the numbers axis.
   * Accepts standard font names.
   * @return String The name of the font.
   */
  public final String getNumbersAxisLabelsFontName() {
    return numbersAxis.labelsFontName;
  }


  /**
   * Gets the color of the font of the labels of the numbers axis.
   * @return Color The color of the font.
   */
  public final Color getNumbersAxisLabelsFontColor() {
    return numbersAxis.labelsFontColor;
  }


  /**
   * Gets the style of the font of the numbers axis labels.
   * Accepts standard font styles.
   * @return int The style of the font.
   */
  public final int getNumbersAxisLabelsFontStyle() {
    return numbersAxis.labelsFontStyle;
  }


  /**
   * Gets whether a gap between the labels or ticks exists, vertically.
   * The gap will be applied to whichever are naturally closer.
   * @return boolean If true, the gap exists.
   */
  public final boolean getNumbersAxisBetweenLabelsOrTicksGapExistence() {
    return numbersAxis.betweenLabelsOrTicksGapExistence;
  }


  /**
   * Gets the thickness of the gap between the labels or ticks,
   * vertically, for the chart's model size.  The gap will be applied to
   * whichever are naturally closer.
   * @return int The model thickness of the gap.
   */
  public final int getNumbersAxisBetweenLabelsOrTicksGapThicknessModel() {
    return numbersAxis.betweenLabelsOrTicksGapThicknessModel;
  }


  /**
   * Gets whether a gap between the labels and ticks exists,
   * horizontally.
   * @return boolean If true, the gap exists.
   */
  public final boolean getNumbersAxisBetweenLabelsAndTicksGapExistence() {
    return numbersAxis.betweenLabelsAndTicksGapExistence;
  }


  /**
   * Gets the thickness of the gap between the labels and ticks,
   * horizontally, for the chart's model size.
   * @return int The model thickness of the gap.
   */
  public final int getNumbersAxisBetweenLabelsAndTicksGapThicknessModel() {
    return numbersAxis.betweenLabelsAndTicksGapThicknessModel;
  }


  /**
   * Gets whether the graph components have different colors across sets (or across cats).
   * @return If true, then colors across sets.
   */
  public final boolean getGraphComponentsColoringByCat() {
    return graphComponentsColoringByCat;
  }


  /**
   * Sets the color properties for the colors by cat coloring.
   * @return The properties of the colors by cat coloring.
   */
  public final MultiColorsProperties getGraphComponentsColorsByCat() {
    return graphComponentsColorsByCat;
  }


  /**
   * Gets whether this object needs to be updated with new properties.
   * @param graphChart2D The object that may need to be updated.
   * @return If true then needs update.
   */
  final boolean getGraphChart2DNeedsUpdate (GraphChart2D graphChart2D) {

    if (needsUpdate) return true;

    if (graphComponentsColoringByCat &&
      graphComponentsColorsByCat.getObject2DNeedsUpdate (graphChart2D)) return true;

    return graphChart2DTracker.getNeedsUpdate (graphChart2D, false);
  }


  /**
   * Adds a GraphChart2D to the set of objects using these properties.
   * @param graphChart2D The object to add.
   */
  final void addGraphChart2D (GraphChart2D graphChart2D) {

    if (graphComponentsColorsByCat != null) graphComponentsColorsByCat.addObject2D (graphChart2D);
    graphChart2DTracker.add (graphChart2D);
  }


  /**
   * Removes a GraphChart2D from the set of objects using these properties.
   * @param graphChart2D The object to remove.
   */
  final void removeGraphChart2D (GraphChart2D graphChart2D) {

    if (graphComponentsColorsByCat != null) {
      graphComponentsColorsByCat.removeObject2D (graphChart2D);
    }
    graphChart2DTracker.remove (graphChart2D);
  }


  /**
   * Validates the properties of this object.
   * If debug is true then prints a messages indicating whether each property is valid.
   * Returns true if all the properties were valid and false otherwise.
   * @param debug If true then will print status messages.
   * @return If true then valid.
   */
  final boolean validate (boolean debug) {

    if (debug) System.out.println ("Validating GraphChart2DProperties");

    boolean valid = true;

    if (chartGraphableToAvailableRatio < 0f || chartGraphableToAvailableRatio > 1f) {
      valid = false;
      if (debug) System.out.println ("Problem with ChartGraphableToAvailableRatio");
    }
    if (!labelsAxis.validate (debug)) valid = false;
    if (!numbersAxis.validate (debug)) valid = false;
    if (graphComponentsColoringByCat) {

      if (graphComponentsColorsByCat == null) {
        valid = false;
        if (debug) System.out.println ("GraphComponentsColorsByCat == null");
      }
      else if (!graphComponentsColorsByCat.validate (debug)) valid = false;
    }

    if (debug) {
      if (valid) System.out.println ("GraphChart2DProperties was valid");
      else System.out.println ("GraphChart2DProperties was invalid");
    }

    return valid;
  }


  /**
   * Updates the properties of this GraphChart2D.
   * @param graphChart2D The GraphChart2D to update.
   */
  final void updateGraphChart2D (GraphChart2D graphChart2D) {

    if (getGraphChart2DNeedsUpdate (graphChart2D)) {

      if (needsUpdate) {
        graphChart2DTracker.markAllNeedsUpdate();
        needsUpdate = false;
      }

      if (graphComponentsColorsByCat != null) {
        graphComponentsColorsByCat.updateObject2D (graphChart2D);
      }

      graphChart2DTracker.clearNeedsUpdate (graphChart2D);
    }
  }
}

