Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/7 api documentation #19

Merged
merged 3 commits into from
Jun 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.1.0 - Unreleased]
## [0.1.1 - Unreleased]

- [#5](https://github.com/itsallcode/holiday-calculator/issues/5) Support month names.
- [#7](https://github.com/itsallcode/holiday-calculator/issues/7) Added documentation for API users.

## [0.1.0] - 2021-06-05

Expand Down
155 changes: 117 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,107 @@ Holiday-calculator supports the following formula flavors:

See [CHANGELOG.md](CHANGELOG.md).

## Configuration
## Usage

### Gradle

```groovy
repositories {
mavenCentral()
}
dependencies {
compile 'org.itsallcode:holiday-calculator:0.1.0'
}
```

### Maven

```xml
<dependency>
<groupId>org.itsallcode</groupId>
<artifactId>holiday-calculator</artifactId>
<version>0.1.0</version>
</dependency>
```

### Calculating holidays

In order to calculate holidays you must create a holiday definition.
Holiday-calculator supports 4 different flavors of holiday definitions.
For each flavor there is a dedicated class in package `org.itsallcode.holidays.calculator.logic`.

1. class `FixedDateHoliday`: Fixed date holiday definition
2. class `FloatingHoliday`: Floating holiday definition
3. class `EasterBasedHoliday`: Easter-based holiday definition
4. class `OrthodoxEasterBasedHoliday`: Orthodox-Easter-based holiday definition

Section [Configuration file](README.md#flavors) describes the details and parameters for each flavor.

#### Instantiating subclasses of Holiday

The following code sample instantiates one holiday for each of these flavors:

```
import org.itsallcode.holidays.calculator.logic.FixedDateHoliday;
import org.itsallcode.holidays.calculator.logic.FloatingHoliday;
import org.itsallcode.holidays.calculator.logic.EasterBasedHoliday;
import org.itsallcode.holidays.calculator.logic.OrthodoxEasterBasedHoliday;

class MyClass {
public MyClass() {
FixedDateHoliday h1 = new FixedDateHoliday("holiday", "Christmas Eve", 12, 24);
FloatingHoliday h2 = new FloatingHoliday(
"holiday", "Father's Day", 3, DayOfWeek.SUNDAY, Direction.AFTER, 6, 1);
EasterBasedHoliday h3 = new EasterBasedHoliday("holiday", "Good Friday", -2);
OrthodoxEasterBasedHoliday h4 = new OrthodoxEasterBasedHoliday(
"holiday", "Orthodox Good Friday", -2);
}
}
```

#### Parsing a configuration File

Besides creating instances of the subclasses of Holiday you can also use a configuration file to define your personal selection of holidays. Class `HolidaysFileParser` then parses the file and returns a list of holidays:

```
import org.itsallcode.holidays.calculator.logic.parser.HolidaysFileParser

class MyClass {
public MyClass() {
List<Holiday> holidays = new HolidaysFileParser.parse("/path/to/holidays.cfg")
}
}
```

For the four example holidays instantiated above, the content of the file could look like

```
# my holidays

holiday fixed 12 24 Christmas Eve
holiday float 3 SUN after 6 1 Father's Day
holiday easter -2 Good Friday
holiday orthodox-easter -2 Orthodox Good Friday
```

Section [Configuration file](README.md#flavors) describes the syntax in detail.

#### Evaluating a specific holiday for a specific year

In order to evaluate a holiday for the current or any other year and hence get an instance of this holiday, you can just call method `Holiday.of()`, supplying the year as argument:

```
EasterBasedHoliday goodFriday = new EasterBasedHoliday("holiday", "Good Friday", -2);
LocalDate goodFriday2021 = goodFriday.of(2021); // 2021 April 4th
```


### Configuration

User can set up his or her individual personal list of favorite holidays using
the supported formula flavors.

### Configuration file
#### Configuration file

(This section needs to be moved to the WR plugin description)

Expand All @@ -36,7 +131,7 @@ located in data directory next to file `projects.json`.
See WR configuration about how to configure the location of the data
directory.

### Content of configuration file
#### <a name="flavors"></a>Content of configuration file

The configuration file is organized in lines. Each line can contain one of 5
types of content:
Expand All @@ -54,16 +149,17 @@ Whitespace is allowed in most places without changing the nature of the
line. Hence, a line containing nothing but tabs and spaces is still rated to
be an empty line.

#### Comments
##### Comments

Each line can contain an optional comment starting with hash mark `#`.
Holiday-calculator will ignore the rest of the line after and including the hash mark character.
Each line can contain an optional comment starting with hash mark `#`.
Holiday-calculator will ignore the rest of the line after and including the
hash mark character.

#### Holiday definitions
##### Holiday definitions

All holiday definitions start with a *category*. The category is an arbitrary
string of non-whitespace characters. The application evaluating your holidays
might support different categories of holidays, e.g. birthdays, aniversaries,
might support different categories of holidays, e.g. birthdays, anniversaries,
etc. and may display them in different colors. As a default we propose to use
category "holiday".

Expand All @@ -73,21 +169,27 @@ is always a string containing the name of the holiday.

General rules
- All strings except the name of the holiday are case-insensitive.
- In all definitions including the number of a month, January is 1,
December is 12.
- In all definitions including a month the month may be specified using its
English name, a unique abbreviation of the name or the number of a month
with 1 for January and 12 for December.
- Day of month is an integer from 1 to 31.
- Day of week is one of the English names or a unique abbreviation of it.

In the following cases holiday-calculator will log an error message and ignore
the holiday definition:
- if the tag does not match any of the three supported tags
"fixed", "float", "easter"
- if the holiday definition contains illegal numbers, such as month 0 or 13,
day 32, or day 30 for February
- if the day of week does not match the abbreviation of any of the english
- if the day of week does not match the abbreviation of any of the English
names Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
- if the day of week is abbreviated ambiguously, e.g. "T" or "S"
- if the name of a month does not match the abbreviation of any of the English
names January, February, March, April, May, June, July, August, September,
October, November.
- if the name of a month is abbreviated ambiguously, e.g. "Ma" or "Ju"

#### Fixed date holiday definition
##### Fixed date holiday definition

A fixed date holiday definition has the tag "fixed", followed by the numbers
of month and day of month.
Expand All @@ -96,7 +198,7 @@ Syntax: `holiday fixed <month> <day> <name>`

Sample: `holiday fixed 1 1 New Year`

#### Floating holiday definition
##### Floating holiday definition

A floating holiday definition has the tag "float", followed by the offset, the
day of week, the direction "before" or "after", the numbers of month and day
Expand All @@ -115,7 +217,7 @@ Samples:
- `holiday float 2 MON before 12 last-day Second Monday before New Year's eve, December the 31st`
- `holiday float 4 SUNDAY before 12 24 First Advent`

#### Easter-based holiday definition
##### Easter-based holiday definition

An Easter-based holiday definition has the tag "easter", followed by the
offset. The offset is the number of days from Easter Sunday. If offset is
Expand All @@ -128,7 +230,7 @@ Samples:
- `holiday easter -2 Good Friday`
- `holiday easter +49 Pentecost Sunday`

#### Orthodox-Easter-based holiday definition
##### Orthodox-Easter-based holiday definition

An Orthodox-Easter-based holiday definition has the tag "orthodox-easter", followed by the
offset. The offset is the number of days from Easter Sunday. If offset is
Expand All @@ -141,29 +243,6 @@ Samples:
- `holiday orthodox-easter -2 Orthodox Good Friday`
- `holiday orthodox-easter +49 Orthodox Pentecost Monday`

## Usage

### Gradle

```groovy
repositories {
mavenCentral()
}
dependencies {
compile 'org.itsallcode:holiday-calculator:0.1.0'
}
```

### Maven

```xml
<dependency>
<groupId>org.itsallcode</groupId>
<artifactId>holiday-calculator</artifactId>
<version>0.1.0</version>
</dependency>
```

## Development

### Generate / update license header
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ plugins {
}

group 'org.itsallcode'
version = '0.1.0'
version = '0.1.1'

java {
toolchain {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,34 @@
*/
package org.itsallcode.holidays.calculator.logic.parser;

import java.time.DayOfWeek;
import java.util.HashMap;

public class DayOfWeekParser {
public static class AmbigueDayOfWeekAbbreviationException extends RuntimeException {
public class AbbreviationParser<T extends Enum<T>> {

public static class AmbigueAbbreviationException extends RuntimeException {
private static final long serialVersionUID = 1L;

public AmbigueDayOfWeekAbbreviationException(String message) {
public AmbigueAbbreviationException(String message) {
super(message);
}
}

private final HashMap<String, DayOfWeek> cache = new HashMap<>();
public static class InvalidAbbreviationException extends RuntimeException {
private static final long serialVersionUID = 1L;

public InvalidAbbreviationException(String message) {
super(message);
}
}

private final Class<T> clazz;
private final HashMap<String, T> cache = new HashMap<>();

public AbbreviationParser(Class<T> clazz) {
this.clazz = clazz;
}

public DayOfWeek getDayOfWeek(final String prefix) {
public T getEnumFor(final String prefix) {
if (cache.containsKey(prefix)) {
return cache.get(prefix);
}
Expand All @@ -41,20 +54,22 @@ public DayOfWeek getDayOfWeek(final String prefix) {
upper = prefix.toUpperCase();
}

DayOfWeek result = null;
for (final DayOfWeek day : DayOfWeek.values()) {
if (day.toString().toUpperCase().startsWith(upper)) {
T result = null;
for (final T value : clazz.getEnumConstants()) {
if (value.toString().toUpperCase().startsWith(upper)) {
if (result != null) {
throw new AmbigueDayOfWeekAbbreviationException(prefix);
throw new AmbigueAbbreviationException(prefix);
}
result = day;
result = value;
}
}

if (result != null) {
cache.put(prefix, result);
if (result == null) {
throw new InvalidAbbreviationException("Could not find any " + clazz.getSimpleName()
+ " for abbreviation \"" + prefix + "\"");
}

cache.put(prefix, result);
return result;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,20 @@
*/
package org.itsallcode.holidays.calculator.logic.parser;

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

import org.itsallcode.holidays.calculator.logic.Holiday;

public abstract class HolidayMatcher {

abstract Holiday createHoliday(Matcher matcher);

private static final Pattern MONTH_NAME_PATTERN = Pattern.compile(
HolidayParser.MONTH_NAME, Pattern.CASE_INSENSITIVE);

private final AbbreviationParser<Month> monthNameParser = new AbbreviationParser<>(Month.class);
private final Pattern pattern;

protected HolidayMatcher(Pattern pattern) {
Expand All @@ -39,4 +45,16 @@ public Holiday createHoliday(String line) {
return createHoliday(matcher);
}

/**
* @param arg (abbreviated) name of month or number as String
* @return number of month as integer
*/
protected int monthNumber(String arg) {
if (MONTH_NAME_PATTERN.matcher(arg).matches()) {
return monthNameParser.getEnumFor(arg).getValue();
} else {
return Integer.parseInt(arg);
}
}

}
Loading