简体   繁体   中英

RRULE to human readable text

I want to generate a human readable text (natural language) from a RRULE string ( RFC2445 ). I have googled for it, what I have found is only a JavaScript library ( rrule.js ) which can generate such a text. But I need this functionality in Android (Java).

Is there a library wrriten in Java like this ? If not, I heared about a couple of JavaScript engines in Java (Rhino for example) But I'm very worry about my Android app performace because It seems this engines is a bit heavy weight.

Instead of bloating your app with needless js, you could either write your own class to do it for you or use this one I wrote. It worked for all the use cases I tested, but if you find something wrong with it please let me know. It uses JodaTime, but could be adjusted for any other time object. It is also able to adapt to localization (although I haven't had a chance to test that yet). If you need more languages, just add them to the Strings.init() method.

package com.rexios.wealthprojection;

import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.ISODateTimeFormat;

import java.util.Calendar;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by adelory on 7/31/2017.
 */

public class ReadableRRule {

    public static String getReadableRRule(String rrule) throws TextParseException {
        return getReadableRRule(rrule, true);
    }

    public static String getReadableRRule(String rrule, boolean capitalize) throws TextParseException {
        Strings.init(Locale.getDefault().getLanguage());

        HashMap<String, String> attributes = new HashMap<>();
        try {
            String[] attrArray = rrule.replace("RRULE:", "").split("[=;]");

            String key = "";
            String value = "";
            for (String s : attrArray) {
                if (key.isEmpty()) {
                    key = s;
                    continue;
                }
                if (value.isEmpty()) {
                    value = s;
                }

                attributes.put(key, value);
                key = "";
                value = "";
            }

            String interval = "";
            String freq = "";
            String byDay = "";
            String until = "";
            String count = "";
            for (Map.Entry<String, String> entry : attributes.entrySet()) {
                if (entry.getKey().equals("FREQ")) {
                    freq = entry.getValue();
                } else if (entry.getKey().equals("UNTIL")) {
                    until = DateTimeFormat.shortDate().print(ISODateTimeFormat.basicDateTimeNoMillis().parseLocalDate(entry.getValue()));
                } else if (entry.getKey().equals("INTERVAL")) {
                    interval = entry.getValue();
                } else if (entry.getKey().equals("COUNT")) {
                    count = entry.getValue();
                } else if (entry.getKey().equals("BYDAY")) {
                    // TODO: Make this work for monthly stuff
                    byDay = entry.getValue();

                    int num = 0;
                    Matcher matcher = Pattern.compile(".?\\d+").matcher(byDay);
                    if (matcher.find()) {
                        String temp = matcher.group();
                        num = Integer.parseInt(temp);
                        byDay = byDay.replace(temp, "");
                    }

                    boolean abbreviate = true;
                    if (byDay.length() == byDay.replace(",", "").length()) abbreviate = false;
                    byDay = byDay.replace(",", ", ");
                    byDay = byDay.replace("MO", abbreviate ? getShortDayName(0) : getFullDayName(0));
                    byDay = byDay.replace("TU", abbreviate ? getShortDayName(1) : getFullDayName(1));
                    byDay = byDay.replace("WE", abbreviate ? getShortDayName(2) : getFullDayName(2));
                    byDay = byDay.replace("TH", abbreviate ? getShortDayName(3) : getFullDayName(3));
                    byDay = byDay.replace("FR", abbreviate ? getShortDayName(4) : getFullDayName(4));
                    byDay = byDay.replace("SA", abbreviate ? getShortDayName(5) : getFullDayName(5));
                    byDay = byDay.replace("SU", abbreviate ? getShortDayName(6) : getFullDayName(6));

                    if (num != 0) {
                        String numText = "";
                        switch (num) {
                            case -1:
                                numText = Strings.last;
                                break;

                            case 1:
                                numText = Strings.first;
                                break;

                            case 2:
                                numText = Strings.second;
                                break;

                            case 3:
                                numText = Strings.third;
                                break;

                            case 4:
                                numText = Strings.fourth;
                                break;
                        }

                        byDay = "(" + Strings.on + " " + Strings.every + " " + numText + " " + byDay + ")";
                    } else {
                        byDay = Strings.on + " " + byDay;
                    }
                }
            }

            if ((interval.isEmpty() || interval.equals("1")) && byDay.length() - byDay.replace(",", "").length() == 6) {
                freq = "DAILY";
                byDay = "";
            }

            String readableRRule = "";
            switch (freq) {
                case "DAILY":
                    if (interval.isEmpty() || interval.equals("1")) {
                        readableRRule += capitalize ? capitalize(Strings.daily) : Strings.daily;
                    } else {
                        readableRRule += capitalize ? capitalize(Strings.every) : Strings.every;
                        readableRRule += " " + interval + " " + Strings.days;
                    }
                    break;
                case "WEEKLY":
                    if (interval.isEmpty() || interval.equals("1")) {
                        readableRRule += capitalize ? capitalize(Strings.weekly) : Strings.weekly;
                    } else {
                        readableRRule += capitalize ? capitalize(Strings.every) : Strings.every;
                        readableRRule += " " + interval + " " + Strings.weeks;
                    }
                    break;
                case "MONTHLY":
                    if (interval.isEmpty() || interval.equals("1")) {
                        readableRRule += capitalize ? capitalize(Strings.monthly) : Strings.monthly;
                    } else {
                        readableRRule += capitalize ? capitalize(Strings.every) : Strings.every;
                        readableRRule += " " + interval + " " + Strings.months;
                    }
                    break;
                case "YEARLY":
                    if (interval.isEmpty() || interval.equals("1")) {
                        readableRRule += capitalize ? capitalize(Strings.yearly) : Strings.yearly;
                    } else {
                        readableRRule += capitalize ? capitalize(Strings.every) : Strings.every;
                        readableRRule += " " + interval + " " + Strings.years;
                    }
                    break;
            }

            if (!byDay.isEmpty()) {
                readableRRule += " " + byDay;
            }

            if (!until.isEmpty()) {
                readableRRule += "; " + Strings.until + " " + until;
            }

            if (!count.isEmpty()) {
                if (count.equals("1")) {
                    readableRRule += "; " + Strings.four + " " + Strings.one + " " + Strings.time;
                } else {
                    readableRRule += "; " + Strings.four + " " + count + " " + Strings.times;
                }
            }

            return readableRRule;
        } catch (Exception e) {
            e.printStackTrace();
            throw new TextParseException();
        }
    }

    // Get day of week Monday-Sunday (0-6)
    private static String getFullDayName(int day) {
        Calendar c = Calendar.getInstance();
        c.set(2011, 7, 1, 0, 0, 0);
        c.add(Calendar.DAY_OF_MONTH, day);
        return String.format("%tA", c);
    }

    private static String getShortDayName(int day) {
        Calendar c = Calendar.getInstance();
        c.set(2011, 7, 1, 0, 0, 0);
        c.add(Calendar.DAY_OF_MONTH, day);
        return String.format("%ta", c);
    }

    private static String capitalize(String s) {
        return s.substring(0, 1).toUpperCase() + s.substring(1);
    }

    private static class Strings {

        static String daily = "daily";
        static String weekly = "weekly";
        static String monthly = "monthly";
        static String yearly = "yearly";

        static String days = "days";
        static String weeks = "weeks";
        static String months = "months";
        static String years = "years";

        static String every = "every";
        static String on = "on";
        static String until = "until";
        static String four = "for"; // "for" is a reserved word
        static String times = "times";
        static String time = "time";

        static String one = "one";
        static String first = "first";
        static String second = "second";
        static String third = "third";
        static String fourth = "fourth";
        static String last = "last";

        static void init(String language) {
            // TODO: Support different locales
            if (language.equals("es")) {
                daily = "diariamente";
                weekly = "semanal";
                monthly = "mensual";
                yearly = "anual";

                days = "días";
                weeks = "semanas";
                months = "meses";
                years = "años";

                every = "cada";
                on = "en";
                until = "hasta";
                four = "para";
                times = "veces";
                time = "vez";

                one = "una";
                first = "primero";
                second = "segundo";
                third = "tercero";
                fourth = "cuarto";
                last = "último";
            }
        }
    }

    public static class TextParseException extends Exception {
        TextParseException() {
            super();
        }

        TextParseException(String message) {
            super(message);
        }
    }
}

Use a WebView and rrule.js inside to get the result. See this answer: How to call javascript from Android?

The next version of Recurrent library will have a deparse() function. It's in python.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM