Package org.projectforge.gantt

Source Code of org.projectforge.gantt.GanttChartXLabelBarRenderer

/////////////////////////////////////////////////////////////////////////////
//
// Project ProjectForge Community Edition
//         www.projectforge.org
//
// Copyright (C) 2001-2014 Kai Reinhard (k.reinhard@micromata.de)
//
// ProjectForge is dual-licensed.
//
// This community edition is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
// by the Free Software Foundation; version 3 of the License.
//
// This community edition 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 General
// Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, see http://www.gnu.org/licenses/.
//
/////////////////////////////////////////////////////////////////////////////

package org.projectforge.gantt;

import java.util.Calendar;
import java.util.Date;

import org.projectforge.calendar.DayHolder;
import org.projectforge.common.DateHolder;
import org.projectforge.export.SVGColor;
import org.projectforge.export.SVGHelper;
import org.projectforge.user.PFUserContext;
import org.projectforge.web.calendar.DateTimeFormatter;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

public class GanttChartXLabelBarRenderer
{
  private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(GanttChartXLabelBarRenderer.class);

  private static final double MIN_PIXEL_PER_UNIT = 10.0;

  private static final int[] QUARTER_SCALES = { 1, 2, 4, 8, 12};

  private static final int[] MONTH_SCALES = { 1, 2, 3, 6, 12, 24, 36, 48, 60};

  private static final int[] SCALES = { 1, 2, 5, 10, 20, 50, 100, 200, 500, 1000};

  GanttXUnit labelUnit;

  int labelScale;

  // Ticks with labels
  GanttXUnit ticksUnit;

  int ticksScale;

  // Small ticks
  GanttXUnit ticksUnit2;

  int ticksScale2;

  GanttXUnit gridUnit;

  int gridScale;

  String label;

  private boolean showNonWorkingDays;

  private Date fromDate;

  private Date toDate;

  int fromToDays = -1;

  private double diagramWidth;

  private GanttChartStyle style;

  public GanttChartXLabelBarRenderer(final Date fromDate, final Date toDate, final double diagramWidth, final GanttChartStyle style)
  {
    this.fromDate = fromDate;
    this.toDate = toDate;
    this.diagramWidth = diagramWidth;
    this.style = style;
    this.gridScale = style.getXGridScale();
    init();
  }

  private void init()
  {

    final int numberOfDays = getFromToDays(); // Initialize fromToDays.
    final double numberOfWeeks = 0.25 * numberOfDays;
    final double numberOfMonths = 0.033 * numberOfDays;
    final double numberOfQuarters = numberOfMonths / 3;
    if (this.style.getXUnit() == GanttXUnit.AUTO) {
      if (diagramWidth / numberOfDays > MIN_PIXEL_PER_UNIT) {
        labelUnit = GanttXUnit.DAY;
        ticksUnit = GanttXUnit.DAY;
        ticksUnit2 = GanttXUnit.DAY;
      } else if (diagramWidth / numberOfWeeks > MIN_PIXEL_PER_UNIT) {
        labelUnit = GanttXUnit.WEEK;
        ticksUnit = GanttXUnit.WEEK;
        ticksUnit2 = GanttXUnit.DAY;
      } else if (diagramWidth / numberOfMonths > MIN_PIXEL_PER_UNIT) {
        labelUnit = GanttXUnit.MONTH;
        ticksUnit = GanttXUnit.MONTH;
        ticksUnit2 = GanttXUnit.WEEK;
      } else {
        labelUnit = GanttXUnit.QUARTER;
        ticksUnit = GanttXUnit.MONTH;
        ticksUnit2 = GanttXUnit.WEEK;
      }
    }
    this.gridUnit = ticksUnit;
    double numberOfUnits;
    if (labelUnit == GanttXUnit.DAY) {
      numberOfUnits = (double) numberOfDays;
    } else if (labelUnit == GanttXUnit.WEEK) {
      numberOfUnits = numberOfWeeks;
    } else if (labelUnit == GanttXUnit.MONTH) {
      numberOfUnits = numberOfMonths;
    } else {
      numberOfUnits = numberOfQuarters;
    }
    labelScale = style.getXLabelsScale();
    ticksScale = style.getXTicksScale();
    ticksScale2 = style.getXTicksScale2();
    if (labelUnit == GanttXUnit.QUARTER) {
      if (labelScale < 0) {
        if (style.isRelativeTimeValues() == true) {
          // Numbers doesn't need as much space as quarter labels (03/2010)
          labelScale = getScale(numberOfQuarters, 50, labelUnit);
        } else {
          labelScale = getScale(numberOfQuarters, 100, labelUnit);
        }
      }
      if (ticksScale < 0) {
        ticksScale = 4;
        if (labelScale <= ticksScale) {
          ticksScale = labelScale;
        }
      }
      if (ticksScale2 < 0) {
        ticksScale = 1;
      }
    } else {
      if (labelScale < 0) {
        if (style.isRelativeTimeValues() == true) {
          // Numbers doesn't need as much space as date labels (03/2010)
          labelScale = getScale(numberOfUnits, 50, labelUnit);
        } else {
          labelScale = getScale(numberOfUnits, 100, labelUnit);
        }
      }
      if (ticksScale < 0) {
        ticksScale = 5;
        if (labelScale <= ticksScale) {
          ticksScale = labelScale;
        }
      }
      if (ticksScale2 < 0) {
        ticksScale = 1;
      }
    }
    if (style.getXLabel() != null) {
      label = style.getXLabel();
    } else if (style.isRelativeTimeValues() == true) {
      label = PFUserContext.getLocalizedString(labelUnit.getI18nKey());
    }
    this.showNonWorkingDays = style.isShowNonWorkingDays();
    if (this.showNonWorkingDays == true) {
      if (diagramWidth / fromToDays < 2) {
        // Don't show non working days due to the large scale. At minimum 3 pixels per day required.
        this.showNonWorkingDays = false;
      }
    }
  }

  public int getScale(final double numberOfUnits, final int minPixels, final GanttXUnit unit)
  {
    final int[] scales;
    if (unit == GanttXUnit.QUARTER) {
      scales = QUARTER_SCALES;
    } else if (unit == GanttXUnit.MONTH) {
      scales = MONTH_SCALES;
    } else {
      scales = SCALES;
    }
    double widthPerUnit = diagramWidth / numberOfUnits;
    int current = 1;
    for (int scale : scales) {
      current = scale;
      if (widthPerUnit * scale > minPixels) {
        break;
      }
    }
    return current;
  }

  /**
   * @param doc
   * @param g1
   * @param grid If null then no grid will be drawn.
   * @param xGridHeight
   */
  public void draw(final Document doc, final Element g1, final Element grid, final double xGridHeight)
  {
    if (label != null) {
      g1.appendChild(SVGHelper.createText(doc, diagramWidth / 2, 0, label));
    }
    final Element ticks = SVGHelper.createElement(doc, "g", "stroke", SVGColor.BLACK.getName(), "stroke-width", "1", "transform", "translate(0,10)");
    g1.appendChild(ticks);
    final DayHolder day = new DayHolder(fromDate);
    final DayHolder toDay = new DayHolder(toDate);
    int dayCounter = 0;
    int weekCounter = 0;
    int monthCounter = 0;
    int quarterCounter = 0;
    int lastDateLabel = 0;
    boolean nonWorkingDayDisplayed = false;
    while (day.before(toDay) == true) {
      if (dayCounter > 0) {
        if (showNonWorkingDays == true && xGridHeight > 0) {
          if (day.isWorkingDay() == true) {
            nonWorkingDayDisplayed = false;
          } else if (nonWorkingDayDisplayed == false) {
            // Non-working day:
            showNonWorkingDays(doc, grid, day.getDate(), toDay.getDate(), xGridHeight);
            nonWorkingDayDisplayed = true;
          }
        }
        int wc = -1;
        if (day.getDayOfWeek() == day.getCalendar().getFirstDayOfWeek()) {
          wc = ++weekCounter;
        }
        int mc = -1;
        int qc = -1;
        final int dayOfMonth = day.getDayOfMonth();
        final int month = day.getMonth();
        if (dayOfMonth == 1) {
          mc = ++monthCounter;
          if (month == Calendar.JANUARY || month == Calendar.APRIL || month == Calendar.JULY || month == Calendar.OCTOBER) {
            qc = ++quarterCounter;
          }
        }
        boolean drawLabel = match(labelScale, labelUnit, dayCounter, wc, mc, qc);
        boolean drawTick = match(ticksScale, ticksUnit, dayCounter, wc, mc, qc);
        boolean drawTick2 = match(ticksScale2, ticksUnit2, dayCounter, wc, mc, qc);
        boolean drawGrid = match(gridScale, gridUnit, dayCounter, wc, mc, qc);
        if (style.isRelativeTimeValues() == false) {
          if (labelUnit == GanttXUnit.DAY) {
            if (labelScale <= 5) {
              // So draw labels on 1st, 5th, 10th, 15th, 20th, 25th of month.
              drawLabel = (dayOfMonth == 1 || dayOfMonth == 5 || dayOfMonth == 10 || dayOfMonth == 15 || dayOfMonth == 20 || dayOfMonth == 25);
            } else if (labelScale <= 15) {
              // So draw labels on 1st, 15th.
              drawLabel = (dayOfMonth == 1 || dayOfMonth == 15);
            } else if (labelScale <= 15) {
              // So draw labels on 1st.
              drawLabel = (dayOfMonth == 1);
            } else {
              if (dayOfMonth == 1 && dayCounter > lastDateLabel + labelScale) {
                drawLabel = true;
                lastDateLabel = dayCounter;
              } else {
                drawLabel = false;
              }
            }
          } else if (labelUnit == GanttXUnit.WEEK) {
            if (dayOfMonth == 1 && dayCounter > lastDateLabel + labelScale) {
              drawLabel = true;
              lastDateLabel = dayCounter;
            } else {
              drawLabel = false;
            }
          }
        }
        if (drawLabel == true) {
          String label = null;
          if (style.isRelativeTimeValues() == false) {
            label = DateTimeFormatter.instance().getFormattedDate(day.getDate());
          } else if (labelUnit == GanttXUnit.DAY) {
            label = String.valueOf(dayCounter);
          } else if (labelUnit == GanttXUnit.WEEK) {
            label = String.valueOf(weekCounter);
          } else if (labelUnit == GanttXUnit.MONTH) {
            label = String.valueOf(monthCounter);
          } else {
            label = String.valueOf(quarterCounter);
          }
          g1.appendChild(SVGHelper.createText(doc, getXValue(day.getDate()), 22, label, "text-anchor", "middle"));
        }
        if (drawLabel == true) {
          ticks.appendChild(SVGHelper.createLine(doc, getXValue(day.getDate()), 12, getXValue(day.getDate()), 20));
        } else if (drawTick == true) {
          ticks.appendChild(SVGHelper.createLine(doc, getXValue(day.getDate()), 15, getXValue(day.getDate()), 20));
        } else if (drawTick2 == true) {
          ticks.appendChild(SVGHelper.createLine(doc, getXValue(day.getDate()), 18, getXValue(day.getDate()), 20));
        }
        if (grid != null && (drawGrid == true || drawLabel == true)) {
          grid.appendChild(SVGHelper.createLine(doc, getXValue(day.getDate()), 0, getXValue(day.getDate()), xGridHeight));
        }
      }
      day.add(Calendar.DAY_OF_MONTH, 1);
      if (dayCounter++ > 5000) {
        log.error("Endless loop detection while creating x tick labels. Breaking.");
        break;
      }
    }
    ticks.appendChild(SVGHelper.createLine(doc, 0, 0, diagramWidth, 0));
    ticks.appendChild(SVGHelper.createLine(doc, 0, 0, 0, 20));
  }

  /**
   * @param doc
   * @param g
   * @param day
   * @param toDate Last day of diagram.
   * @param lastNonWorkingDay
   * @param height
   */
  private void showNonWorkingDays(final Document doc, final Element g, final Date day, final Date toDate, final double height)
  {
    if (g == null) {
      return;
    }
    final DayHolder dh = new DayHolder(day);
    final double x1 = getXValue(day);
    for (int i = 0; i < 100; i++) { // End-less loop protection.
      dh.add(Calendar.DAY_OF_MONTH, 1);
      if (dh.isWorkingDay() == true || dh.before(toDate) == false) {
        break;
      }
    }
    final double x2 = getXValue(dh.getDate());
    g.appendChild(SVGHelper.createRect(doc, x1, 0, x2 - x1, height, SVGColor.LIGHT_GRAY, SVGColor.NONE));
  }

  private boolean match(final int scale, final GanttXUnit unit, final int dayCounter, final int weekCounter, final int monthCounter,
      final int quarterCounter)
  {
    if (unit == GanttXUnit.DAY) {
      if (dayCounter % scale == 0) {
        return true;
      }
    } else if (unit == GanttXUnit.WEEK) {
      if (weekCounter > 0 && weekCounter % scale == 0) {
        return true;
      }
    } else if (unit == GanttXUnit.MONTH) {
      if (monthCounter > 0 && monthCounter % scale == 0) {
        return true;
      }
    } else if (unit == GanttXUnit.QUARTER) {
      if (quarterCounter > 0 && quarterCounter % scale == 0) {
        return true;
      }
    }
    return false;
  }

  private double getXValue(final Date date)
  {
    if (date == null) {
      return 0.0;
    }
    final DateHolder dh = new DateHolder(fromDate);
    final int days = dh.daysBetween(date);
    final int fromToDays = getFromToDays();
    if (fromToDays == 0) {
      return 0;
    }
    return diagramWidth * days / fromToDays;
  }

  private int getFromToDays()
  {
    if (fromToDays < 0) {
      final DateHolder dh = new DateHolder(fromDate);
      fromToDays = dh.daysBetween(toDate);
    }
    return fromToDays;
  }
}
TOP

Related Classes of org.projectforge.gantt.GanttChartXLabelBarRenderer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.