Package org.exist.xquery.functions.fn

Source Code of org.exist.xquery.functions.fn.FnFormatDates

/*
*  eXist Open Source Native XML Database
*  Copyright (C) 2012 The eXist Project
*  http://exist-db.org
*
*  This program 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
*  of the License, or (at your option) any later version.
*
*  This program 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*
*  $Id$
*/
package org.exist.xquery.functions.fn;

import org.exist.dom.QName;
import org.exist.xquery.*;
import org.exist.xquery.util.NumberFormatter;
import org.exist.xquery.value.*;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class FnFormatDates extends BasicFunction {
 
  private static FunctionParameterSequenceType DATETIME = 
    new FunctionParameterSequenceType(
      "value", Type.DATE_TIME, Cardinality.ZERO_OR_ONE, "The datetime");

  private static FunctionParameterSequenceType DATE = 
    new FunctionParameterSequenceType(
      "value", Type.DATE, Cardinality.ZERO_OR_ONE, "The date");
 
  private static FunctionParameterSequenceType TIME = 
    new FunctionParameterSequenceType(
      "value", Type.TIME, Cardinality.ZERO_OR_ONE, "The time");
 
  private static FunctionParameterSequenceType PICTURE =
    new FunctionParameterSequenceType(
      "picture", Type.STRING, Cardinality.EXACTLY_ONE, "The picture string");
 
  private static FunctionParameterSequenceType LANGUAGE =
    new FunctionParameterSequenceType(
      "language", Type.STRING, Cardinality.ZERO_OR_ONE, "The language string");

  private static FunctionParameterSequenceType CALENDAR =
    new FunctionParameterSequenceType(
      "calendar", Type.STRING, Cardinality.ZERO_OR_ONE, "The calendar string");

  private static FunctionParameterSequenceType PLACE =
    new FunctionParameterSequenceType(
      "place", Type.STRING, Cardinality.ZERO_OR_ONE, "The place string");

  private static FunctionReturnSequenceType RETURN =
    new FunctionReturnSequenceType(
      Type.STRING, Cardinality.EXACTLY_ONE, "The formatted date");

    public final static FunctionSignature signatures[] = {
            new FunctionSignature(
                    new QName("format-dateTime", Function.BUILTIN_FUNCTION_NS),
                    "Returns a string containing an xs:date value formatted for display.",
                    new SequenceType[] {
                    DATETIME,
                        PICTURE
                    },
                    RETURN
            ),
            new FunctionSignature(
                    new QName("format-dateTime", Function.BUILTIN_FUNCTION_NS),
                    "Returns a string containing an xs:date value formatted for display.",
                    new SequenceType[] {
                    DATETIME,
                        PICTURE,
                        LANGUAGE,
                        CALENDAR,
                        PLACE
                    },
                    RETURN
            ),
            new FunctionSignature(
                    new QName("format-date", Function.BUILTIN_FUNCTION_NS),
                    "Returns a string containing an xs:date value formatted for display.",
                    new SequenceType[] {
                      DATE,
                        PICTURE
                    },
                    RETURN
            ),
            new FunctionSignature(
                    new QName("format-date", Function.BUILTIN_FUNCTION_NS),
                    "Returns a string containing an xs:date value formatted for display.",
                    new SequenceType[] {
                      DATE,
                        PICTURE,
                        LANGUAGE,
                        CALENDAR,
                        PLACE
                    },
                    RETURN
            ),
            new FunctionSignature(
                    new QName("format-time", Function.BUILTIN_FUNCTION_NS),
                    "Returns a string containing an xs:time value formatted for display.",
                    new SequenceType[] {
                        TIME,
                        PICTURE
                    },
                    RETURN
            ),
            new FunctionSignature(
                    new QName("format-time", Function.BUILTIN_FUNCTION_NS),
                    "Returns a string containing an xs:time value formatted for display.",
                    new SequenceType[] {
                        TIME,
                        PICTURE,
                        LANGUAGE,
                        CALENDAR,
                        PLACE
                    },
                    RETURN
            )
    };

    private static Pattern componentPattern = Pattern.compile("([YMDdWwFHhmsfZzPCE])\\s*(.*)");

    public FnFormatDates(XQueryContext context, FunctionSignature signature) {
        super(context, signature);
    }

    @Override
    public Sequence eval(Sequence[] args, Sequence contextSequence) throws XPathException {
        if (args[0].isEmpty())
            {return Sequence.EMPTY_SEQUENCE;}

        final AbstractDateTimeValue value = (AbstractDateTimeValue) args[0].itemAt(0);
        final String picture = args[1].getStringValue();
        String language = "en";
        if (getArgumentCount() == 5) {
            if (args[2].hasOne())
                {language = args[2].getStringValue();}
        }

        return new StringValue(formatDate(picture, value, language));
    }

    private String formatDate(String pic, AbstractDateTimeValue dt, String language) throws XPathException {
        final StringBuilder sb = new StringBuilder();
        int i = 0;
        while (true) {
            while (i < pic.length() && pic.charAt(i) != '[') {
                sb.append(pic.charAt(i));
                if (pic.charAt(i) == ']') {
                    i++;
                    if (i == pic.length() || pic.charAt(i) != ']') {
                        throw new XPathException(this, ErrorCodes.FOFD1340, "Closing ']' in date picture must be written as ']]'");
                    }
                }
                i++;
            }
            if (i == pic.length()) {
                break;
            }
            // look for '[['
            i++;
            if (i < pic.length() && pic.charAt(i) == '[') {
                sb.append('[');
                i++;
            } else {
                final int close = (i < pic.length() ? pic.indexOf("]", i) : -1);
                if (close == -1) {
                    throw new XPathException(this, ErrorCodes.FOFD1340, "Date format contains a '[' with no matching ']'");
                }
                final String component = pic.substring(i, close);
                formatComponent(component, dt, language, sb);
                i = close + 1;
            }
        }
        return sb.toString();
    }

    private void formatComponent(String component, AbstractDateTimeValue dt, String language, StringBuilder sb) throws XPathException {
        final Matcher matcher = componentPattern.matcher(component);
        if (!matcher.matches())
            {throw new XPathException(this, ErrorCodes.FOFD1340, "Unrecognized date/time component: " + component);}

        final char specifier = component.charAt(0);
        String width = null;
        String picture = matcher.group(2);
        // check if there's an optional width specifier
        final int widthSep = picture.indexOf(',');
        if (-1 < widthSep) {
            width = picture.substring(widthSep + 1);
            picture = picture.substring(0, widthSep);
        }
        // get default format picture if none was specified
        if (picture == null || picture.length() == 0) {
            picture = getDefaultFormat(specifier);
        }
        final boolean allowDate = !Type.subTypeOf(dt.getType(), Type.TIME);
        final boolean allowTime = !Type.subTypeOf(dt.getType(), Type.DATE);
        switch (specifier) {
            case 'Y':
                if (allowDate) {
                    final int year = dt.getPart(AbstractDateTimeValue.YEAR);
                    formatNumber(specifier, picture, width, year, language, sb);
                } else {
                    throw new XPathException(this, ErrorCodes.FOFD1350, "format-time does not support a year component");
                }
                break;
            case 'M':
                if (allowDate) {
                    final int month = dt.getPart(AbstractDateTimeValue.MONTH);
                    formatNumber(specifier, picture, width, month, language, sb);
                } else {
                    throw new XPathException(this, ErrorCodes.FOFD1350, "format-time does not support a month component");
                }
                break;
            case 'D':
                if (allowDate) {
                    final int day = dt.getPart(AbstractDateTimeValue.DAY);
                    formatNumber(specifier, picture, width, day, language, sb);
                } else {
                    throw new XPathException(this, ErrorCodes.FOFD1350, "format-time does not support a day component");
                }
                break;
            case 'd':
                if (allowDate) {
                    final int dayInYear = dt.getDayWithinYear();
                    formatNumber(specifier, picture, width, dayInYear, language, sb);
                } else {
                    throw new XPathException(this, ErrorCodes.FOFD1350, "format-time does not support a day component");
                }
                break;
            case 'W':
                if (allowDate) {
                    final int week = dt.getWeekWithinYear();
                    formatNumber(specifier, picture, width, week, language, sb);
                } else {
                    throw new XPathException(this, ErrorCodes.FOFD1350, "format-time does not support a week component");
                }
                break;
            case 'w':
                if (allowDate) {
                    final int week = dt.getWeekWithinMonth();
                    formatNumber(specifier, picture, width, week, language, sb);
                } else {
                    throw new XPathException(this, ErrorCodes.FOFD1350, "format-time does not support a week component");
                }
                break;
            case 'F':
                if (allowDate) {
                    final int day = dt.getDayOfWeek();
                    formatNumber(specifier, picture, width, day, language, sb);
                } else {
                    throw new XPathException(this, ErrorCodes.FOFD1350, "format-time does not support a day component");
                }
                break;
            case 'H':
                if (allowTime) {
                    final int hour = dt.getPart(AbstractDateTimeValue.HOUR);
                    formatNumber(specifier, picture, width, hour, language, sb);
                } else {
                    throw new XPathException(this, ErrorCodes.FOFD1350, "format-date does not support a hour component");
                }
                break;
            case 'h':
                if (allowTime) {
                    int hour = dt.getPart(AbstractDateTimeValue.HOUR) % 12;
                    if (hour == 0)
                        {hour = 12;}
                    formatNumber(specifier, picture, width, hour, language, sb);
                } else {
                    throw new XPathException(this, ErrorCodes.FOFD1350, "format-date does not support a hour component");
                }
                break;
            case 'm':
                if (allowTime) {
                    final int minute = dt.getPart(AbstractDateTimeValue.MINUTE);
                    formatNumber(specifier, picture, width, minute, language, sb);
                } else {
                    throw new XPathException(this, ErrorCodes.FOFD1350, "format-date does not support a minute component");
                }
                break;
            case 's':
                if (allowTime) {
                    final int second = dt.getPart(AbstractDateTimeValue.SECOND);
                    formatNumber(specifier, picture, width, second, language, sb);
                } else {
                    throw new XPathException(this, ErrorCodes.FOFD1350, "format-date does not support a second component");
                }
                break;
            case 'f':
                if (allowTime) {
                    final int fraction = dt.getPart(AbstractDateTimeValue.MILLISECOND);
                    formatNumber(specifier, picture, width, fraction, language, sb);
                } else {
                    throw new XPathException(this, ErrorCodes.FOFD1350,
                            "format-date does not support a fractional seconds component");
                }
                break;
            case 'P':
                if (allowTime) {
                    final int hour = dt.getPart(AbstractDateTimeValue.HOUR);
                    formatNumber(specifier, picture, width, hour, language, sb);
                } else {
                    throw new XPathException(this, ErrorCodes.FOFD1350,
                            "format-date does not support an am/pm component");
                }
                break;
            case 'z':
                if(dt.getTimezone() != Sequence.EMPTY_SEQUENCE) {
                    sb.append("GMT");
                }
            case 'Z':
                final Sequence tz = dt.getTimezone();
                if(tz != Sequence.EMPTY_SEQUENCE) {
                    final DayTimeDurationValue dtv = ((DayTimeDurationValue)tz);
                    final NumberFormatter formatter = NumberFormatter.getInstance(language);
                    sb.append(dtv.getPart(DurationValue.SIGN) >= 0 ? '+' : '-');
                    sb.append(formatter.formatNumber(dtv.getPart(DurationValue.HOUR), "01", 2, 2));
                    sb.append(':');
                    sb.append(formatter.formatNumber(dtv.getPart(DurationValue.MINUTE), "01", 2, 2));
                }
                break;

            default:
                throw new XPathException(this, ErrorCodes.FOFD1340, "Unrecognized date/time component: " + component);
        }
    }

    private String getDefaultFormat(char specifier) {
        switch (specifier) {
            case 'F':
                return "Nn";
            case 'P':
                return "n";
            case 'C':
            case 'E':
                return "N";
            case 'm':
            case 's':
                return "01";
            default:
                return "1";
        }
    }

    private void formatNumber(char specifier, String picture, String width, int num, String language,
                              StringBuilder sb) throws XPathException {
        final NumberFormatter formatter = NumberFormatter.getInstance(language);
        if ("N".equals(picture) || "n".equals(picture) || "Nn".equals(picture)) {
            String name;
            switch (specifier) {
                case 'M':
                    name = formatter.getMonth(num);
                    break;
                case 'F':
                    name = formatter.getDay(num);
                    break;
                case 'P':
                    name = formatter.getAmPm(num);
                    break;
                default:
                    name = "";
                    break;
            }
            if ("N".equals(picture))
                {name = name.toUpperCase();}
            if ("n".equals(picture))
                {name = name.toLowerCase();}
            sb.append(name);
            return;
        }

        // determine min and max width
        int min = NumberFormatter.getMinDigits(picture);
        int max = NumberFormatter.getMaxDigits(picture);
        if (max == 1)
            {max = Integer.MAX_VALUE;}
        // explicit width takes precedence
        final int widths[] = getWidths(width);
        if (widths != null) {
            if (widths[0] > 0) {min = widths[0];}
            if (widths[1] > 0) {max = widths[1];}
        }
        try {
            sb.append(formatter.formatNumber(num, picture, min, max));
        } catch (final XPathException e) {
            throw new XPathException(this, ErrorCodes.FOFD1350, e.getMessage());
        }
    }

    private int[] getWidths(String width) throws XPathException {
        if (width == null || width.length() == 0)
            {return null;}

        int min = -1;
        int max = -1;
        String minPart = width;
        String maxPart = null;
        final int p = width.indexOf('-');
        if (p < 0) {
            minPart = width;
        } else {
            minPart = width.substring(0, p);
            maxPart = width.substring(p + 1);
        }
        if ("*".equals(minPart))
            {min = 1;}
        else {
            try {
                min = Integer.parseInt(minPart);
            } catch (final NumberFormatException e) {

            }
        }
        if (maxPart != null) {
            if ("*".equals(maxPart))
                {max = Integer.MAX_VALUE;}
            else {
                try {
                    max = Integer.parseInt(maxPart);
                } catch (final NumberFormatException e) {
                }
            }
        }
        if (max != -1 && min > max)
            {throw new XPathException(this, ErrorCodes.FOFD1350,"Minimum width > maximum width in component");}
        return new int[] { min, max };
    }
}
TOP

Related Classes of org.exist.xquery.functions.fn.FnFormatDates

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.