001/* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-present, by David Gilbert and Contributors.
006 *
007 * Project Info:  http://www.jfree.org/jfreechart/index.html
008 *
009 * This library is free software; you can redistribute it and/or modify it
010 * under the terms of the GNU Lesser General Public License as published by
011 * the Free Software Foundation; either version 2.1 of the License, or
012 * (at your option) any later version.
013 *
014 * This library is distributed in the hope that it will be useful, but
015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
017 * License for more details.
018 *
019 * You should have received a copy of the GNU Lesser General Public
020 * License along with this library; if not, write to the Free Software
021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
022 * USA.
023 *
024 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 
025 * Other names may be trademarks of their respective owners.]
026 *
027 * --------------------
028 * MonthDateFormat.java
029 * --------------------
030 * (C) Copyright 2005-present, by David Gilbert and Contributors.
031 *
032 * Original Author:  David Gilbert;
033 * Contributor(s):   -;
034 *
035 */
036
037package org.jfree.chart.axis;
038
039import java.text.DateFormat;
040import java.text.DateFormatSymbols;
041import java.text.FieldPosition;
042import java.text.NumberFormat;
043import java.text.ParsePosition;
044import java.text.SimpleDateFormat;
045import java.util.Arrays;
046import java.util.Calendar;
047import java.util.Date;
048import java.util.GregorianCalendar;
049import java.util.Locale;
050import java.util.TimeZone;
051import org.jfree.chart.util.Args;
052
053/**
054 * A formatter that formats dates to show the initial letter(s) of the month
055 * name and, as an option, the year for the first or last month of each year.
056 */
057public class MonthDateFormat extends DateFormat {
058
059    /** The symbols used for the months. */
060    private String[] months;
061
062    /** Flags that control which months will have the year appended. */
063    private boolean[] showYear;
064
065    /** The year formatter. */
066    private DateFormat yearFormatter;
067
068    /**
069     * Creates a new instance for the default time zone.
070     */
071    public MonthDateFormat() {
072        this(TimeZone.getDefault());
073    }
074
075    /**
076     * Creates a new instance for the specified time zone.
077     *
078     * @param zone  the time zone ({@code null} not permitted).
079     */
080    public MonthDateFormat(TimeZone zone) {
081        this(zone, Locale.getDefault(), 1, true, false);
082    }
083
084    /**
085     * Creates a new instance for the specified time zone.
086     *
087     * @param locale  the locale used to obtain the month
088     *                names ({@code null} not permitted).
089     */
090    public MonthDateFormat(Locale locale) {
091        this(TimeZone.getDefault(), locale, 1, true, false);
092    }
093
094    /**
095     * Creates a new instance for the specified time zone.
096     *
097     * @param zone  the time zone ({@code null} not permitted).
098     * @param chars  the maximum number of characters to use from the month
099     *               names (that are obtained from the date symbols of the
100     *               default locale).  If this value is <= 0, the entire
101     *               month name is used in each case.
102     */
103    public MonthDateFormat(TimeZone zone, int chars) {
104        this(zone, Locale.getDefault(), chars, true, false);
105    }
106
107    /**
108     * Creates a new instance for the specified time zone.
109     *
110     * @param locale  the locale ({@code null} not permitted).
111     * @param chars  the maximum number of characters to use from the month
112     *               names (that are obtained from the date symbols of the
113     *               default locale).  If this value is <= 0, the entire
114     *               month name is used in each case.
115     */
116    public MonthDateFormat(Locale locale, int chars) {
117        this(TimeZone.getDefault(), locale, chars, true, false);
118    }
119
120    /**
121     * Creates a new formatter.
122     *
123     * @param zone  the time zone used to extract the month and year from dates
124     *              passed to this formatter ({@code null} not permitted).
125     * @param locale  the locale used to determine the month names
126     *                ({@code null} not permitted).
127     * @param chars  the maximum number of characters to use from the month
128     *               names, or zero to indicate that the entire month name
129     *               should be used.
130     * @param showYearForJan  a flag that controls whether or not the year is
131     *                        appended to the symbol for the first month of
132     *                        each year.
133     * @param showYearForDec  a flag that controls whether or not the year is
134     *                        appended to the symbol for the last month of
135     *                        each year.
136     */
137    public MonthDateFormat(TimeZone zone, Locale locale, int chars,
138                           boolean showYearForJan, boolean showYearForDec) {
139        this(zone, locale, chars, new boolean[] {showYearForJan, false, false,
140            false, false, false, false, false, false, false, false, false,
141            showYearForDec}, new SimpleDateFormat("yy"));
142    }
143
144    /**
145     * Creates a new formatter.
146     *
147     * @param zone  the time zone used to extract the month and year from dates
148     *              passed to this formatter ({@code null} not permitted).
149     * @param locale  the locale used to determine the month names
150     *                ({@code null} not permitted).
151     * @param chars  the maximum number of characters to use from the month
152     *               names, or zero to indicate that the entire month name
153     *               should be used.
154     * @param showYear  an array of flags that control whether or not the
155     *                  year is displayed for a particular month.
156     * @param yearFormatter  the year formatter.
157     */
158    public MonthDateFormat(TimeZone zone, Locale locale, int chars,
159                           boolean[] showYear, DateFormat yearFormatter) {
160        Args.nullNotPermitted(locale, "locale");
161        DateFormatSymbols dfs = new DateFormatSymbols(locale);
162        String[] monthsFromLocale = dfs.getMonths();
163        this.months = new String[12];
164        for (int i = 0; i < 12; i++) {
165            if (chars > 0) {
166                this.months[i] = monthsFromLocale[i].substring(0,
167                        Math.min(chars, monthsFromLocale[i].length()));
168            }
169            else {
170                this.months[i] = monthsFromLocale[i];
171            }
172        }
173        this.calendar = new GregorianCalendar(zone);
174        this.showYear = showYear;
175        this.yearFormatter = yearFormatter;
176
177        // the following is never used, but it seems that DateFormat requires
178        // it to be non-null.  It isn't well covered in the spec, refer to
179        // bug parade 5061189 for more info.
180        this.numberFormat = NumberFormat.getNumberInstance();
181    }
182
183    /**
184     * Formats the given date.
185     *
186     * @param date  the date.
187     * @param toAppendTo  the string buffer.
188     * @param fieldPosition  the field position.
189     *
190     * @return The formatted date.
191     */
192    @Override
193    public StringBuffer format(Date date, StringBuffer toAppendTo,
194                               FieldPosition fieldPosition) {
195        this.calendar.setTime(date);
196        int month = this.calendar.get(Calendar.MONTH);
197        toAppendTo.append(this.months[month]);
198        if (this.showYear[month]) {
199            toAppendTo.append(this.yearFormatter.format(date));
200        }
201        return toAppendTo;
202    }
203
204    /**
205     * Parses the given string (not implemented).
206     *
207     * @param source  the date string.
208     * @param pos  the parse position.
209     *
210     * @return {@code null}, as this method has not been implemented.
211     */
212    @Override
213    public Date parse(String source, ParsePosition pos) {
214        return null;
215    }
216
217    /**
218     * Tests this formatter for equality with an arbitrary object.
219     *
220     * @param obj  the object.
221     *
222     * @return A boolean.
223     */
224    @Override
225    public boolean equals(Object obj) {
226        if (obj == this) {
227            return true;
228        }
229        if (!(obj instanceof MonthDateFormat)) {
230            return false;
231        }
232        if (!super.equals(obj)) {
233            return false;
234        }
235        MonthDateFormat that = (MonthDateFormat) obj;
236        if (!Arrays.equals(this.months, that.months)) {
237            return false;
238        }
239        if (!Arrays.equals(this.showYear, that.showYear)) {
240            return false;
241        }
242        if (!this.yearFormatter.equals(that.yearFormatter)) {
243            return false;
244        }
245        return true;
246    }
247}