diff --git a/jollyday-core/src/main/java/module-info.java b/jollyday-core/src/main/java/module-info.java
index 349e83b0d..6a59dbf9c 100644
--- a/jollyday-core/src/main/java/module-info.java
+++ b/jollyday-core/src/main/java/module-info.java
@@ -16,8 +16,10 @@
de.focus_shift.jollyday.jaxb,
de.focus_shift.jollyday.jackson,
de.focus_shift.jollyday.jackson.test,
- de.focus_shift.jollyday.jaxb.test;
+ de.focus_shift.jollyday.jaxb.test,
+ de.focus_shift.jollyday.java.test;
exports de.focus_shift.jollyday.core.parser.impl to
de.focus_shift.jollyday.jackson.test,
- de.focus_shift.jollyday.jaxb.test;
+ de.focus_shift.jollyday.jaxb.test,
+ de.focus_shift.jollyday.java.test;
}
diff --git a/jollyday-jackson/src/main/java/module-info.java b/jollyday-jackson/src/main/java/module-info.java
index fd9b2b091..5ca33ab60 100644
--- a/jollyday-jackson/src/main/java/module-info.java
+++ b/jollyday-jackson/src/main/java/module-info.java
@@ -20,8 +20,10 @@
exports de.focus_shift.jollyday.jackson to
de.focus_shift.jollyday.core,
- de.focus_shift.jollyday.jackson.test;
+ de.focus_shift.jollyday.jackson.test,
+ de.focus_shift.jollyday.pojo.test;
exports de.focus_shift.jollyday.jackson.mapping to
com.fasterxml.jackson.databind,
- de.focus_shift.jollyday.jackson.test;
+ de.focus_shift.jollyday.jackson.test,
+ de.focus_shift.jollyday.pojo.test;
}
diff --git a/jollyday-pojo-generator/pom.xml b/jollyday-pojo-generator/pom.xml
new file mode 100644
index 000000000..f0fdd668e
--- /dev/null
+++ b/jollyday-pojo-generator/pom.xml
@@ -0,0 +1,90 @@
+
+ 4.0.0
+
+
+ de.focus-shift
+ jollyday
+ 1.5.0-SNAPSHOT
+ ../pom.xml
+
+
+ jollyday-pojo-generator
+
+ Jollyday with Plain Old Java Objects Maven Plugin Generator
+ Maven Plugin that generates Plain Old Java Objects from xml holiday configuration
+
+ maven-plugin
+
+
+ ${maven.version}
+
+
+
+ 3.9.6
+
+
+
+
+
+ de.focus-shift
+ jollyday-core
+ ${project.version}
+
+
+
+ de.focus-shift
+ jollyday-jackson
+ ${project.version}
+
+
+
+ org.apache.maven
+ maven-plugin-api
+ ${maven.version}
+ provided
+
+
+ org.apache.maven
+ maven-core
+ ${maven.version}
+ provided
+
+
+ org.apache.maven
+ maven-artifact
+ ${maven.version}
+ provided
+
+
+
+ org.apache.maven.plugin-tools
+ maven-plugin-annotations
+ 3.11.0
+ provided
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-plugin-plugin
+ 3.11.0
+
+ pojo-generator
+ true
+
+
+
+ mojo-descriptor
+ package
+
+ descriptor
+
+
+
+
+
+
+
diff --git a/jollyday-pojo-generator/src/main/java/de/focus_shift/jollyday/pojo/generator/PojoGenerator.java b/jollyday-pojo-generator/src/main/java/de/focus_shift/jollyday/pojo/generator/PojoGenerator.java
new file mode 100644
index 000000000..eecafce67
--- /dev/null
+++ b/jollyday-pojo-generator/src/main/java/de/focus_shift/jollyday/pojo/generator/PojoGenerator.java
@@ -0,0 +1,376 @@
+package de.focus_shift.jollyday.pojo.generator;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Writer;
+import java.time.DayOfWeek;
+import java.time.Month;
+import java.time.MonthDay;
+import java.time.Year;
+import java.time.chrono.Chronology;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.threeten.extra.Days;
+
+import de.focus_shift.jollyday.core.HolidayCalendar;
+import de.focus_shift.jollyday.core.spi.ChristianHoliday;
+import de.focus_shift.jollyday.core.spi.Configuration;
+import de.focus_shift.jollyday.core.spi.EthiopianOrthodoxHoliday;
+import de.focus_shift.jollyday.core.spi.Fixed;
+import de.focus_shift.jollyday.core.spi.FixedWeekdayBetweenFixed;
+import de.focus_shift.jollyday.core.spi.FixedWeekdayInMonth;
+import de.focus_shift.jollyday.core.spi.FixedWeekdayRelativeToFixed;
+import de.focus_shift.jollyday.core.spi.Holidays;
+import de.focus_shift.jollyday.core.spi.IslamicHoliday;
+import de.focus_shift.jollyday.core.spi.Limited.YearCycle;
+import de.focus_shift.jollyday.core.spi.Movable.MovingCondition;
+import de.focus_shift.jollyday.core.spi.RelativeToEasterSunday;
+import de.focus_shift.jollyday.core.spi.RelativeToFixed;
+import de.focus_shift.jollyday.core.spi.RelativeToWeekdayInMonth;
+import de.focus_shift.jollyday.jackson.JacksonConfiguration;
+import de.focus_shift.jollyday.jackson.JacksonXMLMapper;
+
+class PojoGenerator {
+
+ private static final char[] hexChar = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
+ 'E', 'F' };
+
+ void generateHolidaySource(HolidayCalendar cal, Writer writer) throws IOException {
+ JacksonXMLMapper xmlUtil = new JacksonXMLMapper();
+
+ String calendarId = cal.getId().toLowerCase();
+ String holidayFileName = "Holidays_" + calendarId + ".xml";
+
+ InputStream inputStream = PojoGenerator.class.getClassLoader().getResourceAsStream("holidays/" + holidayFileName);
+ if (inputStream == null) {
+ System.err.println("No input found for " + holidayFileName);
+ return;
+ }
+ JacksonConfiguration jacksonConfiguration = new JacksonConfiguration(xmlUtil.unmarshallConfiguration(inputStream));
+
+ writer.write("package de.focus_shift.jollyday.pojo.holidays;\n\n");
+ writeImports(writer);
+ writer.write("import de.focus_shift.jollyday.pojo.*;\n\n");
+ writer.write("public class Holiday_" + calendarId + " {\n\n");
+
+ writer.write(" public static PojoConfiguration configuration;\n\n");
+ StringBuilder sb = new StringBuilder();
+ sb.append(" static {\n");
+ sb.append(" configuration = ");
+ sb.append(configuration(jacksonConfiguration));
+ sb.append(";\n");
+ sb.append(" }\n");
+ sb.append("}\n");
+
+ writer.write(sb.toString());
+ }
+
+ private String unicodeEscape(String value) {
+ if (value == null || value.isBlank()) {
+ return value;
+ }
+
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < value.length(); i++) {
+ char c = value.charAt(i);
+ if ((c >> 7) > 0) {
+ sb.append("\\u");
+ sb.append(hexChar[(c >> 12) & 0xF]); // append the hex character for the left-most 4-bits
+ sb.append(hexChar[(c >> 8) & 0xF]); // hex for the second group of 4-bits from the left
+ sb.append(hexChar[(c >> 4) & 0xF]); // hex for the third group
+ sb.append(hexChar[c & 0xF]); // hex for the last group, e.g., the right most 4-bits
+ } else {
+ sb.append(c);
+ }
+ }
+ return sb.toString();
+ }
+
+ void generateConfigurationSource(Writer writer) throws IOException {
+
+ writeHeader(writer);
+
+ writer.append(" static Map configurations = new HashMap<>();\n");
+
+ writer.append(" static {\n");
+ for (HolidayCalendar cal : HolidayCalendar.values()) {
+ String calendarId = cal.getId().toLowerCase();
+ writer.write(String.format(" configurations.put(\"%s\",Holiday_%s.configuration);\n", calendarId, calendarId));
+ }
+ writer.write(" }\n");
+ writeFooter(writer);
+ }
+
+ private void writeImports(Writer writer) throws IOException {
+ StringBuilder sb = new StringBuilder();
+ sb.append("import java.time.DayOfWeek;\n");
+ sb.append("import java.time.Month;\n");
+ sb.append("import java.time.MonthDay;\n");
+ sb.append("import java.time.Year;\n");
+ sb.append("import java.time.chrono.Chronology;\n");
+ sb.append("import java.util.HashMap;\n");
+ sb.append("import java.util.List;\n");
+ sb.append("import java.util.Map;\n\n");
+
+ sb.append("import de.focus_shift.jollyday.core.HolidayType;\n");
+ sb.append("import de.focus_shift.jollyday.core.ManagerParameter;\n");
+ sb.append("import de.focus_shift.jollyday.core.spi.ChristianHoliday.ChristianHolidayType;\n");
+ sb.append("import de.focus_shift.jollyday.core.spi.Configuration;\n");
+ sb.append("import de.focus_shift.jollyday.core.spi.ConfigurationService;\n");
+ sb.append("import de.focus_shift.jollyday.core.spi.EthiopianOrthodoxHoliday.EthiopianOrthodoxHolidayType;\n");
+ sb.append("import de.focus_shift.jollyday.core.spi.IslamicHoliday.IslamicHolidayType;\n");
+ sb.append("import de.focus_shift.jollyday.core.spi.Occurrence;\n");
+ sb.append("import de.focus_shift.jollyday.core.spi.Relation;\n");
+ sb.append("import de.focus_shift.jollyday.core.spi.Movable.MovingCondition.With;\n");
+ sb.append("import de.focus_shift.jollyday.core.spi.Limited.YearCycle;\n");
+
+ writer.write(sb.toString());
+ }
+
+ private void writeHeader(Writer writer) throws IOException {
+ writer.write("package de.focus_shift.jollyday.pojo;\n\n");
+
+ writer.write("import java.util.HashMap;\n");
+ writer.write("import java.util.Map;\n\n");
+
+ writer.write("import de.focus_shift.jollyday.core.ManagerParameter;\n");
+ writer.write("import de.focus_shift.jollyday.core.spi.Configuration;\n");
+ writer.write("import de.focus_shift.jollyday.core.spi.ConfigurationService;\n");
+ writer.write("import de.focus_shift.jollyday.pojo.holidays.*;\n\n");
+
+ writer.write("public class PojoConfigurationService implements ConfigurationService {\n\n");
+ }
+
+ private void writeFooter(Writer writer) throws IOException {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append("\n");
+ sb.append(" @Override\n");
+ sb.append(" public Configuration getConfiguration(ManagerParameter parameter) {\n");
+ sb.append(" final String cacheKey = parameter.createCacheKey();\n");
+ sb.append("\n");
+ sb.append(" PojoConfiguration configuration = configurations.get(cacheKey);\n");
+ sb.append(" return configuration;\n");
+ sb.append(" }\n");
+ sb.append("}");
+
+ writer.write(sb.toString());
+ }
+
+ // public PojoConfiguration(PojoHolidays javaHolidays, List subConfigurations, String hierarchy, String description)
+ private String configuration(Configuration configuration) {
+ return constructor("PojoConfiguration", holidays(configuration.holidays()), configurations(configuration.subConfigurations()), string(configuration.hierarchy()), string(configuration.description()));
+ }
+
+ private String configurations(Stream configurations) {
+ String result;
+ if (configurations != null) {
+ result = configurations.map(c -> configuration(c)).collect(Collectors.joining(",", "List.of(", ")"));
+ if ("List.of()".equals(result)) {
+ result = "null";
+ }
+ } else {
+ result = "null";
+ }
+
+ return result;
+ }
+
+ // public PojoHolidays(List christianHoliday, List islamicHoliday, List ethiopianOrthodoxHoliday, List fixed, List fixedWeekday, List fixedWeekdayBetweenFixed, List fixedWeekdayRelativeToFixed, List relativeToFixed, List relativeToWeekdayInMonth, List relativeToEasterSunday)
+ private String holidays(Holidays holidays) {
+ if (holidays == null) {
+ return "null";
+ }
+
+ StringBuilder sb = new StringBuilder();
+ sb.append(String.format("new PojoHolidays()\n"));
+ for (de.focus_shift.jollyday.core.spi.Fixed fixed : holidays.fixed()) {
+ sb.append(String.format(" .addFixed(%s)\n", fixed(fixed)));
+ }
+
+ for (ChristianHoliday christianHoliday : holidays.christianHolidays()) {
+ sb.append(String.format(" .addChristianHoliday(%s)\n", christianHoliday(christianHoliday)));
+ }
+
+ for (EthiopianOrthodoxHoliday ethiopianOrthodoxHoliday : holidays.ethiopianOrthodoxHolidays()) {
+ sb.append(String.format(" .addEthiopianOrthodoxHoliday(%s)\n", ethiopianOrthodoxHoliday(ethiopianOrthodoxHoliday)));
+ }
+
+ for (IslamicHoliday islamicHoliday : holidays.islamicHolidays()) {
+ sb.append(String.format(" .addIslamicHoliday(%s)\n", islamicHoliday(islamicHoliday)));
+ }
+
+ for (FixedWeekdayBetweenFixed fixedWeekdayBetweenFixed : holidays.fixedWeekdayBetweenFixed()) {
+ sb.append(String.format(" .addFixedWeekdayBetweenFixed(%s)\n", fixedWeekdayBetweenFixed(fixedWeekdayBetweenFixed)));
+ }
+
+ for (FixedWeekdayInMonth fixedWeekdayInMonth : holidays.fixedWeekdays()) {
+ sb.append(String.format(" .addFixedWeekday(%s)\n", fixedWeekdayInMonth(fixedWeekdayInMonth)));
+ }
+
+ for (FixedWeekdayRelativeToFixed fixedWeekdayRelativeToFixed : holidays.fixedWeekdayRelativeToFixed()) {
+ sb.append(String.format(" .addFixedWeekdayRelativeToFixed(%s)\n", fixedWeekdayRelativeToFixed(fixedWeekdayRelativeToFixed)));
+ }
+
+ for (RelativeToEasterSunday relativeToEasterSunday : holidays.relativeToEasterSunday()) {
+ sb.append(String.format(" .addRelativeToEasterSunday(%s)\n", relativeToEasterSunday(relativeToEasterSunday)));
+ }
+
+ for (RelativeToFixed relativeToFixed : holidays.relativeToFixed()) {
+ sb.append(String.format(" .addRelativeToFixed(%s)\n", relativeToFixed(relativeToFixed)));
+ }
+
+ for (RelativeToWeekdayInMonth relativeToWeekdayInMonth : holidays.relativeToWeekdayInMonth()) {
+ sb.append(String.format(" .addRelativeToWeekdayInMonth(%s)\n", relativeToWeekdayInMonth(relativeToWeekdayInMonth)));
+ }
+
+ return sb.toString();
+ }
+
+ // public PojoFixed(String descriptionPropertiesKey, HolidayType officiality, Year validFrom, Year validTo, YearCycle cycle, List conditions, MonthDay day)
+ private String fixed(Fixed fixed) {
+ return constructor("PojoFixed", string(fixed.descriptionPropertiesKey()), enums(fixed.holidayType()), year(fixed.validFrom()), year(fixed.validTo()), yearCycle(fixed.cycle()), movingConditions(fixed.conditions()), monthDay(fixed.day()));
+ }
+
+ //public PojoChristianHoliday(String descriptionPropertiesKey, HolidayType officiality, Year validFrom, Year validTo, YearCycle cycle, List conditions, ChristianHolidayType type, Chronology chronology)
+ private String christianHoliday(ChristianHoliday christianHoliday) {
+ return constructor("PojoChristianHoliday", string(christianHoliday.descriptionPropertiesKey()), enums(christianHoliday.holidayType()), year(christianHoliday.validFrom()), year(christianHoliday.validTo()), yearCycle(christianHoliday.cycle()), movingConditions(christianHoliday.conditions()), enums(christianHoliday.type()), chronology(christianHoliday.chronology()));
+ }
+
+ // public PojoEthiopianOrthodoxHoliday(String descriptionPropertiesKey, HolidayType officiality, Year validFrom, Year validTo, YearCycle cycle, EthiopianOrthodoxHolidayType type)
+ private String ethiopianOrthodoxHoliday(EthiopianOrthodoxHoliday hol) {
+ return constructor("PojoEthiopianOrthodoxHoliday", string(hol.descriptionPropertiesKey()), enums(hol.holidayType()), year(hol.validFrom()), year(hol.validTo()), yearCycle(hol.cycle()), enums(hol.type()));
+ }
+
+ // public PojoIslamicHoliday(String descriptionPropertiesKey, HolidayType officiality, Year validFrom, Year validTo, YearCycle cycle, List conditions, IslamicHolidayType type)
+ private String islamicHoliday(IslamicHoliday hol) {
+ return constructor("PojoIslamicHoliday", string(hol.descriptionPropertiesKey()), enums(hol.holidayType()), year(hol.validFrom()), year(hol.validTo()), yearCycle(hol.cycle()), movingConditions(hol.conditions()), enums(hol.type()));
+ }
+
+ // public PojoFixedWeekdayBetweenFixed(String descriptionPropertiesKey, HolidayType officiality, Year validFrom, Year validTo, YearCycle cycle, Fixed from, Fixed to, DayOfWeek weekday)
+ private String fixedWeekdayBetweenFixed(FixedWeekdayBetweenFixed hol) {
+ return constructor("PojoFixedWeekdayBetweenFixed", string(hol.descriptionPropertiesKey()), enums(hol.holidayType()), year(hol.validFrom()), year(hol.validTo()), yearCycle(hol.cycle()), fixed(hol.from()), fixed(hol.to()), dayOfWeek(hol.weekday()));
+ }
+
+ // public PojoFixedWeekdayInMonth(String descriptionPropertiesKey, HolidayType officiality, Year validFrom, Year validTo, YearCycle cycle, DayOfWeek weekday, Month month, Occurrence which)
+ private String fixedWeekdayInMonth(FixedWeekdayInMonth hol) {
+ return constructor("PojoFixedWeekdayInMonth", string(hol.descriptionPropertiesKey()), enums(hol.holidayType()), year(hol.validFrom()), year(hol.validTo()), yearCycle(hol.cycle()), dayOfWeek(hol.weekday()), month(hol.month()), enums(hol.which()));
+ }
+
+ // public PojoFixedWeekdayRelativeToFixed(String descriptionPropertiesKey, HolidayType officiality, Year validFrom, Year validTo, YearCycle cycle, DayOfWeek weekday, Relation when, Fixed day, Occurrence which)
+ private String fixedWeekdayRelativeToFixed(FixedWeekdayRelativeToFixed hol) {
+ return constructor("PojoFixedWeekdayRelativeToFixed", string(hol.descriptionPropertiesKey()), enums(hol.holidayType()), year(hol.validFrom()), year(hol.validTo()), yearCycle(hol.cycle()), dayOfWeek(hol.weekday()), enums(hol.when()), fixed(hol.day()), enums(hol.which()));
+ }
+
+ // public PojoRelativeToEasterSunday(String descriptionPropertiesKey, HolidayType officiality, Year validFrom, Year validTo, YearCycle cycle, Chronology chronology, Days days)
+ private String relativeToEasterSunday(RelativeToEasterSunday hol) {
+ return constructor("PojoRelativeToEasterSunday", string(hol.descriptionPropertiesKey()), enums(hol.holidayType()), year(hol.validFrom()), year(hol.validTo()), yearCycle(hol.cycle()), chronology(hol.chronology()), days(hol.days()));
+ }
+
+ // public PojoRelativeToFixed(String descriptionPropertiesKey, HolidayType officiality, Year validFrom, Year validTo, YearCycle cycle, Fixed date, DayOfWeek weekday, Relation when, Days days) {
+ private String relativeToFixed(RelativeToFixed hol) {
+ return constructor("PojoRelativeToFixed", string(hol.descriptionPropertiesKey()), enums(hol.holidayType()), year(hol.validFrom()), year(hol.validTo()), yearCycle(hol.cycle()), fixed(hol.date()), dayOfWeek(hol.weekday()), enums(hol.when()), days(hol.days()));
+ }
+
+ // public PojoRelativeToWeekdayInMonth(String descriptionPropertiesKey, HolidayType officiality, Year validFrom, Year validTo, YearCycle cycle, FixedWeekdayInMonth weekdayInMonth, DayOfWeek weekday, Relation when)
+ private String relativeToWeekdayInMonth(RelativeToWeekdayInMonth hol) {
+ return constructor("PojoRelativeToWeekdayInMonth", string(hol.descriptionPropertiesKey()), enums(hol.holidayType()), year(hol.validFrom()), year(hol.validTo()), yearCycle(hol.cycle()), fixedWeekdayInMonth(hol.weekdayInMonth()), dayOfWeek(hol.weekday()), enums(hol.when()));
+ }
+
+ private String constructor(Object... arguments) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("new %s(");
+ for (int i = 0; i < arguments.length - 1; i++) {
+ sb.append("%s,");
+ }
+ sb.setLength(sb.length() - 1);
+ sb.append(")");
+
+ return String.format(sb.toString(), arguments);
+ }
+
+ private String string(String string) {
+ return String.format("\"%s\"", unicodeEscape(string));
+ }
+
+ private String year(Year year) {
+ if (year != null) {
+ return String.format("Year.of(%s)", year.getValue());
+ } else {
+ return "null";
+ }
+ }
+
+ private String enums(Enum enumz) {
+ return enumz.getDeclaringClass().getSimpleName() + "." + enumz.name();
+ }
+
+ private String yearCycle(YearCycle yearCycle) {
+ return enums(yearCycle);
+ }
+
+ private String days(Days days) {
+ if (days != null) {
+ return String.format("Days.of(%s)", days.getAmount());
+ } else {
+ return "null";
+ }
+ }
+
+ private String monthDay(MonthDay monthDay) {
+ if (monthDay != null) {
+ return String.format("MonthDay.of(%s,%s)", monthDay.getMonthValue(), monthDay.getDayOfMonth());
+ } else {
+ return "null";
+ }
+ }
+
+ private String month(Month monthDay) {
+ if (monthDay != null) {
+ return String.format("Month.of(%s)", monthDay.getValue());
+ } else {
+ return "null";
+ }
+ }
+
+ private String dayOfWeek(DayOfWeek dayOfWeek) {
+
+ if (dayOfWeek != null) {
+ return String.format("DayOfWeek.of(%s)", dayOfWeek.getValue());
+ } else {
+ return "null";
+ }
+ }
+
+ private String chronology(Chronology chronology) {
+ if (chronology != null) {
+ return String.format("Chronology.of(\"%s\")", chronology.getId());
+ } else {
+ return "null";
+ }
+ }
+
+ private String movingConditions(List movingConditions) {
+ if (movingConditions.isEmpty()) {
+ return "null";
+ } else {
+ StringBuilder sb = new StringBuilder();
+ sb.append("List.of(");
+ movingConditions.forEach(m -> sb.append(movingCondition(m)).append(","));
+ sb.setLength(sb.length() - 1);
+ sb.append(")");
+ return sb.toString();
+ }
+ }
+
+ // public PojoMovingCondition(DayOfWeek substitute, With with, DayOfWeek weekday) {
+ private String movingCondition(MovingCondition movingCondition) {
+ if (movingCondition == null) {
+ return "null";
+ } else {
+ return constructor("PojoMovingCondition", dayOfWeek(movingCondition.substitute()), enums(movingCondition.with()), dayOfWeek(movingCondition.weekday()));
+ }
+ }
+}
diff --git a/jollyday-pojo-generator/src/main/java/de/focus_shift/jollyday/pojo/generator/PojoGeneratorMojo.java b/jollyday-pojo-generator/src/main/java/de/focus_shift/jollyday/pojo/generator/PojoGeneratorMojo.java
new file mode 100644
index 000000000..b5d2a434f
--- /dev/null
+++ b/jollyday-pojo-generator/src/main/java/de/focus_shift/jollyday/pojo/generator/PojoGeneratorMojo.java
@@ -0,0 +1,58 @@
+package de.focus_shift.jollyday.pojo.generator;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.Locale;
+
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.logging.Log;
+import org.apache.maven.plugins.annotations.LifecyclePhase;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+
+import de.focus_shift.jollyday.core.HolidayCalendar;
+
+/**
+ * Generate PojoConfiguration
+ */
+@Mojo(name = "generate", defaultPhase = LifecyclePhase.GENERATE_SOURCES)
+public class PojoGeneratorMojo extends AbstractMojo {
+
+ /**
+ * The directory where the pojos will be generated to
+ */
+ @Parameter(defaultValue = "${project.build.directory}/generated-sources/pojo", property = "outputDir", required = true)
+ private File outputDirectory;
+
+ public void execute() throws MojoExecutionException {
+ final Log log = getLog();
+
+ final File genDir = Paths.get(outputDirectory.getAbsolutePath(), "de/focus_shift/jollyday/pojo/holidays").toFile();
+ if (!genDir.exists() && !genDir.mkdirs()) {
+ throw new MojoExecutionException("Target directory doesn't exist or cannot be generated: "+ genDir.getAbsolutePath());
+ }
+
+ final File configurationService = Paths.get(outputDirectory.getAbsolutePath(), "de/focus_shift/jollyday/pojo/PojoConfigurationService.java").toFile();
+
+ try (FileWriter fileWriter = new FileWriter(configurationService, false)) {
+ PojoGenerator generator = new PojoGenerator();
+ generator.generateConfigurationSource(fileWriter);
+
+ for (HolidayCalendar cal : HolidayCalendar.values()) {
+ String calendarId = cal.getId().toLowerCase(Locale.ROOT);
+
+ final File holidayFile = Paths.get(outputDirectory.getAbsolutePath(), "de/focus_shift/jollyday/pojo/holidays/Holiday_" + calendarId + ".java").toFile();
+ try (FileWriter holidayFileWriter = new FileWriter(holidayFile, false)) {
+ generator.generateHolidaySource(cal, holidayFileWriter);
+ }
+ }
+ } catch (IOException e) {
+ throw new MojoExecutionException(e.getLocalizedMessage());
+ }
+
+ log.info("Holiday files created at "+outputDirectory.getAbsolutePath());
+ }
+}
diff --git a/jollyday-pojo-generator/src/main/resources/META-INF/MANIFEST.MF b/jollyday-pojo-generator/src/main/resources/META-INF/MANIFEST.MF
new file mode 100644
index 000000000..e69de29bb
diff --git a/jollyday-pojo/pom.xml b/jollyday-pojo/pom.xml
new file mode 100644
index 000000000..6ab8b0bd4
--- /dev/null
+++ b/jollyday-pojo/pom.xml
@@ -0,0 +1,118 @@
+
+ 4.0.0
+
+ jollyday-pojo
+ Jollyday with Plain Old Java Objects
+
+ Plain Old Java Objects based jollyday implementation
+
+
+ de.focus-shift
+ jollyday
+ 1.5.0-SNAPSHOT
+ ../pom.xml
+
+
+
+
+ de.focus-shift
+ jollyday-core
+ ${project.version}
+
+
+
+
+
+ org.threeten
+ threeten-extra
+
+
+
+
+ ch.qos.logback
+ logback-classic
+ test
+
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
+
+ org.assertj
+ assertj-core
+ test
+
+
+
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+ 3.2.0
+
+
+ generate-sources
+
+ add-source
+
+
+
+ ${project.build.directory}/generated-sources/pojo
+
+
+
+
+
+
+
+ de.focus-shift
+ jollyday-pojo-generator
+ ${project.version}
+
+
+ ${project.build.directory}/generated-sources/pojo
+
+
+
+
+ generate-const
+ generate-sources
+
+ generate
+
+
+
+
+
+
+ org.apache.felix
+ maven-bundle-plugin
+ true
+
+
+ bundle-manifest
+ process-classes
+
+ manifest
+
+
+
+
+
+ de.focus_shift.jollyday-pojo
+ de.focus_shift.jollday.pojo.*
+ osgi.extender;filter:="(osgi.extender=osgi.serviceloader.registrar)"
+ osgi.serviceloader;osgi.serviceloader=de.focus_shift.jollyday.core.spi.ConfigurationService
+
+
+
+
+
+
+
diff --git a/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/DefaultHoliday.java b/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/DefaultHoliday.java
new file mode 100644
index 000000000..40184466c
--- /dev/null
+++ b/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/DefaultHoliday.java
@@ -0,0 +1,45 @@
+package de.focus_shift.jollyday.pojo;
+
+import java.time.Year;
+
+import de.focus_shift.jollyday.core.HolidayType;
+import de.focus_shift.jollyday.core.spi.Limited.YearCycle;
+
+public abstract class DefaultHoliday {
+
+ private String descriptionPropertiesKey;
+ private HolidayType officiality;
+ private Year validFrom;
+ private Year validTo;
+ private YearCycle cycle;
+
+ public DefaultHoliday(String descriptionPropertiesKey, HolidayType officiality, Year validFrom, Year validTo, YearCycle cycle) {
+ this.descriptionPropertiesKey = descriptionPropertiesKey;
+ this.officiality = officiality;
+ this.validFrom = validFrom;
+ this.validTo = validTo;
+ this.cycle = cycle;
+ }
+
+ public String descriptionPropertiesKey() {
+ return descriptionPropertiesKey;
+ }
+
+ public HolidayType holidayType() {
+ return officiality;
+ }
+
+ public Year validFrom() {
+ return validFrom;
+ }
+
+ public Year validTo() {
+ return validTo;
+ }
+
+ public YearCycle cycle() {
+ return cycle;
+ }
+
+
+}
diff --git a/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/DefaultMovingHoliday.java b/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/DefaultMovingHoliday.java
new file mode 100644
index 000000000..dd203305a
--- /dev/null
+++ b/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/DefaultMovingHoliday.java
@@ -0,0 +1,23 @@
+package de.focus_shift.jollyday.pojo;
+
+import java.time.Year;
+import java.util.Collections;
+import java.util.List;
+
+import de.focus_shift.jollyday.core.HolidayType;
+import de.focus_shift.jollyday.core.spi.Limited.YearCycle;
+import de.focus_shift.jollyday.core.spi.Movable.MovingCondition;
+
+public abstract class DefaultMovingHoliday extends DefaultHoliday {
+
+ private List conditions;
+
+ public DefaultMovingHoliday(String descriptionPropertiesKey, HolidayType officiality, Year validFrom, Year validTo, YearCycle cycle, List conditions) {
+ super(descriptionPropertiesKey, officiality, validFrom, validTo, cycle);
+ this.conditions = conditions;
+ }
+
+ public List conditions() {
+ return conditions != null ? conditions : Collections.emptyList();
+ }
+}
diff --git a/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/PojoChristianHoliday.java b/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/PojoChristianHoliday.java
new file mode 100644
index 000000000..8fe0dbeae
--- /dev/null
+++ b/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/PojoChristianHoliday.java
@@ -0,0 +1,31 @@
+package de.focus_shift.jollyday.pojo;
+
+import java.time.Year;
+import java.time.chrono.Chronology;
+import java.util.List;
+
+import de.focus_shift.jollyday.core.HolidayType;
+import de.focus_shift.jollyday.core.spi.ChristianHoliday;
+
+public class PojoChristianHoliday extends DefaultMovingHoliday implements ChristianHoliday {
+
+ private ChristianHolidayType type;
+ private Chronology chronology;
+
+ public PojoChristianHoliday(String descriptionPropertiesKey, HolidayType officiality, Year validFrom, Year validTo, YearCycle cycle, List conditions, ChristianHolidayType type, Chronology chronology) {
+ super(descriptionPropertiesKey, officiality, validFrom, validTo, cycle, conditions);
+ this.type = type;
+ this.chronology = chronology;
+ }
+
+ @Override
+ public ChristianHolidayType type() {
+ return type;
+ }
+
+ @Override
+ public Chronology chronology() {
+ return chronology;
+ }
+
+}
diff --git a/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/PojoConfiguration.java b/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/PojoConfiguration.java
new file mode 100644
index 000000000..835f47871
--- /dev/null
+++ b/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/PojoConfiguration.java
@@ -0,0 +1,46 @@
+package de.focus_shift.jollyday.pojo;
+
+import java.util.List;
+import java.util.stream.Stream;
+
+import de.focus_shift.jollyday.core.spi.Configuration;
+import de.focus_shift.jollyday.core.spi.Holidays;
+
+public class PojoConfiguration implements Configuration {
+
+ private PojoHolidays javaHolidays;
+ private List subConfigurations;
+ private String hierarchy;
+ private String description;
+
+ public PojoConfiguration() {
+ }
+
+ public PojoConfiguration(PojoHolidays javaHolidays, List subConfigurations, String hierarchy, String description) {
+ this.javaHolidays = javaHolidays;
+ this.subConfigurations = subConfigurations;
+ this.hierarchy = hierarchy;
+ this.description = description;
+ }
+
+ @Override
+ public Holidays holidays() {
+ return javaHolidays;
+ }
+
+ @Override
+ public Stream subConfigurations() {
+ return subConfigurations != null ? subConfigurations.stream() : Stream.empty();
+ }
+
+ @Override
+ public String hierarchy() {
+ return hierarchy;
+ }
+
+ @Override
+ public String description() {
+ return description;
+ }
+
+}
diff --git a/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/PojoEthiopianOrthodoxHoliday.java b/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/PojoEthiopianOrthodoxHoliday.java
new file mode 100644
index 000000000..044a1e033
--- /dev/null
+++ b/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/PojoEthiopianOrthodoxHoliday.java
@@ -0,0 +1,22 @@
+package de.focus_shift.jollyday.pojo;
+
+import java.time.Year;
+
+import de.focus_shift.jollyday.core.HolidayType;
+import de.focus_shift.jollyday.core.spi.EthiopianOrthodoxHoliday;
+
+public class PojoEthiopianOrthodoxHoliday extends DefaultHoliday implements EthiopianOrthodoxHoliday {
+
+ private EthiopianOrthodoxHolidayType type;
+
+ public PojoEthiopianOrthodoxHoliday(String descriptionPropertiesKey, HolidayType officiality, Year validFrom, Year validTo, YearCycle cycle, EthiopianOrthodoxHolidayType type) {
+ super(descriptionPropertiesKey, officiality, validFrom, validTo, cycle);
+ this.type = type;
+ }
+
+ @Override
+ public EthiopianOrthodoxHolidayType type() {
+ return type;
+ }
+
+}
diff --git a/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/PojoFixed.java b/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/PojoFixed.java
new file mode 100644
index 000000000..0d67c5852
--- /dev/null
+++ b/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/PojoFixed.java
@@ -0,0 +1,25 @@
+package de.focus_shift.jollyday.pojo;
+
+import java.time.MonthDay;
+import java.time.Year;
+import java.util.List;
+
+import de.focus_shift.jollyday.core.HolidayType;
+import de.focus_shift.jollyday.core.spi.Fixed;
+
+public class PojoFixed extends DefaultMovingHoliday implements Fixed {
+
+ private MonthDay day;
+
+ public PojoFixed(String descriptionPropertiesKey, HolidayType officiality, Year validFrom, Year validTo, YearCycle cycle, List conditions, MonthDay day) {
+ super(descriptionPropertiesKey, officiality, validFrom, validTo, cycle, conditions);
+ this.day = day;
+ }
+
+ @Override
+ public MonthDay day() {
+ return day;
+ }
+
+
+}
diff --git a/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/PojoFixedWeekdayBetweenFixed.java b/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/PojoFixedWeekdayBetweenFixed.java
new file mode 100644
index 000000000..4d845ef4b
--- /dev/null
+++ b/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/PojoFixedWeekdayBetweenFixed.java
@@ -0,0 +1,39 @@
+package de.focus_shift.jollyday.pojo;
+
+import java.time.DayOfWeek;
+import java.time.Year;
+
+import de.focus_shift.jollyday.core.HolidayType;
+import de.focus_shift.jollyday.core.spi.Fixed;
+import de.focus_shift.jollyday.core.spi.FixedWeekdayBetweenFixed;
+
+public class PojoFixedWeekdayBetweenFixed extends DefaultHoliday implements FixedWeekdayBetweenFixed {
+
+ private Fixed from;
+ private Fixed to;
+ private DayOfWeek weekday;
+
+ public PojoFixedWeekdayBetweenFixed(String descriptionPropertiesKey, HolidayType officiality, Year validFrom, Year validTo, YearCycle cycle, Fixed from, Fixed to, DayOfWeek weekday) {
+ super(descriptionPropertiesKey, officiality, validFrom, validTo, cycle);
+ this.from = from;
+ this.to = to;
+ this.weekday = weekday;
+ }
+
+ @Override
+ public Fixed from() {
+ return from;
+ }
+
+ @Override
+ public Fixed to() {
+ return to;
+ }
+
+ @Override
+ public DayOfWeek weekday() {
+ return weekday;
+ }
+
+
+}
diff --git a/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/PojoFixedWeekdayInMonth.java b/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/PojoFixedWeekdayInMonth.java
new file mode 100644
index 000000000..07563dea6
--- /dev/null
+++ b/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/PojoFixedWeekdayInMonth.java
@@ -0,0 +1,40 @@
+package de.focus_shift.jollyday.pojo;
+
+import java.time.DayOfWeek;
+import java.time.Month;
+import java.time.Year;
+
+import de.focus_shift.jollyday.core.HolidayType;
+import de.focus_shift.jollyday.core.spi.FixedWeekdayInMonth;
+import de.focus_shift.jollyday.core.spi.Occurrence;
+
+public class PojoFixedWeekdayInMonth extends DefaultHoliday implements FixedWeekdayInMonth {
+
+ private DayOfWeek weekday;
+ private Month month;
+ private Occurrence which;
+
+ public PojoFixedWeekdayInMonth(String descriptionPropertiesKey, HolidayType officiality, Year validFrom, Year validTo, YearCycle cycle, DayOfWeek weekday, Month month, Occurrence which) {
+ super(descriptionPropertiesKey, officiality, validFrom, validTo, cycle);
+ this.weekday = weekday;
+ this.month = month;
+ this.which = which;
+ }
+
+ @Override
+ public DayOfWeek weekday() {
+ return weekday;
+ }
+
+ @Override
+ public Month month() {
+ return month;
+ }
+
+ @Override
+ public Occurrence which() {
+ return which;
+ }
+
+
+}
diff --git a/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/PojoFixedWeekdayRelativeToFixed.java b/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/PojoFixedWeekdayRelativeToFixed.java
new file mode 100644
index 000000000..82482a5e9
--- /dev/null
+++ b/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/PojoFixedWeekdayRelativeToFixed.java
@@ -0,0 +1,47 @@
+package de.focus_shift.jollyday.pojo;
+
+import java.time.DayOfWeek;
+import java.time.Year;
+
+import de.focus_shift.jollyday.core.HolidayType;
+import de.focus_shift.jollyday.core.spi.Fixed;
+import de.focus_shift.jollyday.core.spi.FixedWeekdayRelativeToFixed;
+import de.focus_shift.jollyday.core.spi.Occurrence;
+import de.focus_shift.jollyday.core.spi.Relation;
+
+public class PojoFixedWeekdayRelativeToFixed extends DefaultHoliday implements FixedWeekdayRelativeToFixed {
+
+ private DayOfWeek weekday;
+ private Relation when;
+ private Fixed day;
+ private Occurrence which;
+
+ public PojoFixedWeekdayRelativeToFixed(String descriptionPropertiesKey, HolidayType officiality, Year validFrom, Year validTo, YearCycle cycle, DayOfWeek weekday, Relation when, Fixed day, Occurrence which) {
+ super(descriptionPropertiesKey, officiality, validFrom, validTo, cycle);
+ this.weekday = weekday;
+ this.when = when;
+ this.day = day;
+ this.which = which;
+ }
+
+ @Override
+ public DayOfWeek weekday() {
+ return weekday;
+ }
+
+ @Override
+ public Relation when() {
+ return when;
+ }
+
+ @Override
+ public Fixed day() {
+ return day;
+ }
+
+ @Override
+ public Occurrence which() {
+ return which;
+ }
+
+}
diff --git a/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/PojoHolidays.java b/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/PojoHolidays.java
new file mode 100644
index 000000000..54580bd80
--- /dev/null
+++ b/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/PojoHolidays.java
@@ -0,0 +1,148 @@
+package de.focus_shift.jollyday.pojo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import de.focus_shift.jollyday.core.spi.ChristianHoliday;
+import de.focus_shift.jollyday.core.spi.EthiopianOrthodoxHoliday;
+import de.focus_shift.jollyday.core.spi.Fixed;
+import de.focus_shift.jollyday.core.spi.FixedWeekdayBetweenFixed;
+import de.focus_shift.jollyday.core.spi.FixedWeekdayInMonth;
+import de.focus_shift.jollyday.core.spi.FixedWeekdayRelativeToFixed;
+import de.focus_shift.jollyday.core.spi.IslamicHoliday;
+import de.focus_shift.jollyday.core.spi.RelativeToEasterSunday;
+import de.focus_shift.jollyday.core.spi.RelativeToFixed;
+import de.focus_shift.jollyday.core.spi.RelativeToWeekdayInMonth;
+
+public class PojoHolidays implements de.focus_shift.jollyday.core.spi.Holidays {
+
+ protected List christianHoliday = new ArrayList<>();
+ protected List islamicHoliday = new ArrayList<>();
+ protected List ethiopianOrthodoxHoliday = new ArrayList<>();
+
+ protected List fixed = new ArrayList<>();
+ protected List fixedWeekday= new ArrayList<>();
+ protected List fixedWeekdayBetweenFixed= new ArrayList<>();
+ protected List fixedWeekdayRelativeToFixed= new ArrayList<>();
+
+ protected List relativeToFixed = new ArrayList<>();
+ protected List relativeToWeekdayInMonth = new ArrayList<>();
+ protected List relativeToEasterSunday= new ArrayList<>();
+
+ public PojoHolidays() {
+
+ }
+
+ public PojoHolidays(List christianHoliday, List islamicHoliday, List ethiopianOrthodoxHoliday, List fixed, List fixedWeekday, List fixedWeekdayBetweenFixed, List fixedWeekdayRelativeToFixed, List relativeToFixed, List relativeToWeekdayInMonth, List relativeToEasterSunday) {
+ this.christianHoliday = christianHoliday;
+ this.islamicHoliday = islamicHoliday;
+ this.ethiopianOrthodoxHoliday = ethiopianOrthodoxHoliday;
+ this.fixed = fixed;
+ this.fixedWeekday = fixedWeekday;
+ this.fixedWeekdayBetweenFixed = fixedWeekdayBetweenFixed;
+ this.fixedWeekdayRelativeToFixed = fixedWeekdayRelativeToFixed;
+ this.relativeToFixed = relativeToFixed;
+ this.relativeToWeekdayInMonth = relativeToWeekdayInMonth;
+ this.relativeToEasterSunday = relativeToEasterSunday;
+ }
+
+ @Override
+ public List fixed() {
+ return fixed;
+ }
+
+ public PojoHolidays addFixed(Fixed value) {
+ this.fixed.add(value);
+ return this;
+ }
+
+ @Override
+ public List relativeToFixed() {
+ return relativeToFixed;
+ }
+
+ public PojoHolidays addRelativeToFixed(RelativeToFixed value) {
+ this.relativeToFixed.add(value);
+ return this;
+ }
+
+ @Override
+ public List relativeToWeekdayInMonth() {
+ return relativeToWeekdayInMonth;
+ }
+
+ public PojoHolidays addRelativeToWeekdayInMonth(RelativeToWeekdayInMonth value) {
+ this.relativeToWeekdayInMonth.add(value);
+ return this;
+ }
+
+ @Override
+ public List fixedWeekdays() {
+ return fixedWeekday;
+ }
+
+ public PojoHolidays addFixedWeekday(FixedWeekdayInMonth value) {
+ this.fixedWeekday.add(value);
+ return this;
+ }
+
+ @Override
+ public List christianHolidays() {
+ return christianHoliday;
+ }
+
+ public PojoHolidays addChristianHoliday(ChristianHoliday value) {
+ this.christianHoliday.add(value);
+ return this;
+ }
+
+ @Override
+ public List islamicHolidays() {
+ return islamicHoliday;
+ }
+
+ public PojoHolidays addIslamicHoliday(IslamicHoliday value) {
+ this.islamicHoliday.add(value);
+ return this;
+ }
+
+ @Override
+ public List fixedWeekdayBetweenFixed() {
+ return fixedWeekdayBetweenFixed;
+ }
+
+ public PojoHolidays addFixedWeekdayBetweenFixed(FixedWeekdayBetweenFixed value) {
+ this.fixedWeekdayBetweenFixed.add(value);
+ return this;
+ }
+
+ @Override
+ public List fixedWeekdayRelativeToFixed() {
+ return fixedWeekdayRelativeToFixed;
+ }
+
+ public PojoHolidays addFixedWeekdayRelativeToFixed(FixedWeekdayRelativeToFixed value) {
+ this.fixedWeekdayRelativeToFixed.add(value);
+ return this;
+ }
+
+ @Override
+ public List ethiopianOrthodoxHolidays() {
+ return ethiopianOrthodoxHoliday;
+ }
+
+ public PojoHolidays addEthiopianOrthodoxHoliday(EthiopianOrthodoxHoliday value) {
+ this.ethiopianOrthodoxHoliday.add(value);
+ return this;
+ }
+
+ @Override
+ public List relativeToEasterSunday() {
+ return relativeToEasterSunday;
+ }
+
+ public PojoHolidays addRelativeToEasterSunday(RelativeToEasterSunday value) {
+ this.relativeToEasterSunday.add(value);
+ return this;
+ }
+}
diff --git a/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/PojoIslamicHoliday.java b/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/PojoIslamicHoliday.java
new file mode 100644
index 000000000..1f1f10276
--- /dev/null
+++ b/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/PojoIslamicHoliday.java
@@ -0,0 +1,23 @@
+package de.focus_shift.jollyday.pojo;
+
+import java.time.Year;
+import java.util.List;
+
+import de.focus_shift.jollyday.core.HolidayType;
+import de.focus_shift.jollyday.core.spi.IslamicHoliday;
+
+public class PojoIslamicHoliday extends DefaultMovingHoliday implements IslamicHoliday {
+
+ private IslamicHolidayType type;
+
+ public PojoIslamicHoliday(String descriptionPropertiesKey, HolidayType officiality, Year validFrom, Year validTo, YearCycle cycle, List conditions, IslamicHolidayType type) {
+ super(descriptionPropertiesKey, officiality, validFrom, validTo, cycle, conditions);
+ this.type = type;
+ }
+
+ @Override
+ public IslamicHolidayType type() {
+ return type;
+ }
+
+}
diff --git a/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/PojoMovingCondition.java b/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/PojoMovingCondition.java
new file mode 100644
index 000000000..c0f36db60
--- /dev/null
+++ b/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/PojoMovingCondition.java
@@ -0,0 +1,33 @@
+package de.focus_shift.jollyday.pojo;
+
+import java.time.DayOfWeek;
+
+import de.focus_shift.jollyday.core.spi.Movable.MovingCondition;
+
+public class PojoMovingCondition implements MovingCondition {
+
+ private DayOfWeek substitute;
+ private With with;
+ private DayOfWeek weekday;
+
+ public PojoMovingCondition(DayOfWeek substitute, With with, DayOfWeek weekday) {
+ this.substitute = substitute;
+ this.with = with;
+ this.weekday = weekday;
+ }
+
+ @Override
+ public DayOfWeek substitute() {
+ return substitute;
+ }
+
+ @Override
+ public With with() {
+ return with;
+ }
+
+ @Override
+ public DayOfWeek weekday() {
+ return weekday;
+ }
+}
diff --git a/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/PojoRelativeToEasterSunday.java b/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/PojoRelativeToEasterSunday.java
new file mode 100644
index 000000000..dea38f342
--- /dev/null
+++ b/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/PojoRelativeToEasterSunday.java
@@ -0,0 +1,31 @@
+package de.focus_shift.jollyday.pojo;
+
+import java.time.Year;
+import java.time.chrono.Chronology;
+
+import org.threeten.extra.Days;
+
+import de.focus_shift.jollyday.core.HolidayType;
+import de.focus_shift.jollyday.core.spi.RelativeToEasterSunday;
+
+public class PojoRelativeToEasterSunday extends DefaultHoliday implements RelativeToEasterSunday {
+
+ private Chronology chronology;
+ private Days days;
+
+ public PojoRelativeToEasterSunday(String descriptionPropertiesKey, HolidayType officiality, Year validFrom, Year validTo, YearCycle cycle, Chronology chronology, Days days) {
+ super(descriptionPropertiesKey, officiality, validFrom, validTo, cycle);
+ this.chronology = chronology;
+ this.days = days;
+ }
+
+ @Override
+ public Chronology chronology() {
+ return chronology;
+ }
+
+ @Override
+ public Days days() {
+ return days;
+ }
+}
diff --git a/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/PojoRelativeToFixed.java b/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/PojoRelativeToFixed.java
new file mode 100644
index 000000000..0a25b2c56
--- /dev/null
+++ b/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/PojoRelativeToFixed.java
@@ -0,0 +1,49 @@
+package de.focus_shift.jollyday.pojo;
+
+import java.time.DayOfWeek;
+import java.time.Year;
+
+import org.threeten.extra.Days;
+
+import de.focus_shift.jollyday.core.HolidayType;
+import de.focus_shift.jollyday.core.spi.Fixed;
+import de.focus_shift.jollyday.core.spi.Relation;
+import de.focus_shift.jollyday.core.spi.RelativeToFixed;
+
+public class PojoRelativeToFixed extends DefaultHoliday implements RelativeToFixed {
+
+ private Fixed date;
+
+ DayOfWeek weekday;
+ Relation when;
+ Days days;
+
+ public PojoRelativeToFixed(String descriptionPropertiesKey, HolidayType officiality, Year validFrom, Year validTo, YearCycle cycle, Fixed date, DayOfWeek weekday, Relation when, Days days) {
+ super(descriptionPropertiesKey, officiality, validFrom, validTo, cycle);
+ this.date = date;
+ this.weekday = weekday;
+ this.when = when;
+ this.days = days;
+ }
+
+ @Override
+ public Fixed date() {
+ return date;
+ }
+
+ @Override
+ public DayOfWeek weekday() {
+ return weekday;
+ }
+
+ @Override
+ public Relation when() {
+ return when;
+ }
+
+ @Override
+ public Days days() {
+ return days;
+ }
+
+}
diff --git a/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/PojoRelativeToWeekdayInMonth.java b/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/PojoRelativeToWeekdayInMonth.java
new file mode 100644
index 000000000..f02bdaa4e
--- /dev/null
+++ b/jollyday-pojo/src/main/java/de/focus_shift/jollyday/pojo/PojoRelativeToWeekdayInMonth.java
@@ -0,0 +1,40 @@
+package de.focus_shift.jollyday.pojo;
+
+import java.time.DayOfWeek;
+import java.time.Year;
+
+import de.focus_shift.jollyday.core.HolidayType;
+import de.focus_shift.jollyday.core.spi.FixedWeekdayInMonth;
+import de.focus_shift.jollyday.core.spi.Relation;
+import de.focus_shift.jollyday.core.spi.RelativeToWeekdayInMonth;
+
+public class PojoRelativeToWeekdayInMonth extends DefaultHoliday implements RelativeToWeekdayInMonth {
+
+ private FixedWeekdayInMonth weekdayInMonth;
+ private DayOfWeek weekday;
+ private Relation when;
+
+ public PojoRelativeToWeekdayInMonth(String descriptionPropertiesKey, HolidayType officiality, Year validFrom, Year validTo, YearCycle cycle, FixedWeekdayInMonth weekdayInMonth, DayOfWeek weekday, Relation when) {
+ super(descriptionPropertiesKey, officiality, validFrom, validTo, cycle);
+ this.weekdayInMonth = weekdayInMonth;
+ this.weekday = weekday;
+ this.when = when;
+ }
+
+ @Override
+ public FixedWeekdayInMonth weekdayInMonth() {
+ return weekdayInMonth;
+ }
+
+ @Override
+ public DayOfWeek weekday() {
+ return weekday;
+ }
+
+ @Override
+ public Relation when() {
+ return when;
+ }
+
+
+}
diff --git a/jollyday-pojo/src/main/java/module-info.java b/jollyday-pojo/src/main/java/module-info.java
new file mode 100644
index 000000000..1aa4fc32c
--- /dev/null
+++ b/jollyday-pojo/src/main/java/module-info.java
@@ -0,0 +1,18 @@
+import de.focus_shift.jollyday.core.spi.ConfigurationService;
+import de.focus_shift.jollyday.pojo.PojoConfigurationService;
+
+module de.focus_shift.jollyday.pojo {
+
+ provides ConfigurationService with
+ PojoConfigurationService;
+
+ requires org.slf4j;
+ requires org.threeten.extra;
+ requires de.focus_shift.jollyday.core;
+
+ exports de.focus_shift.jollyday.pojo to
+ de.focus_shift.jollyday.core,
+ de.focus_shift.jollyday.pojo.test;
+
+
+}
diff --git a/jollyday-pojo/src/main/resources/META-INF/services/de.focus_shift.jollyday.core.spi.ConfigurationService b/jollyday-pojo/src/main/resources/META-INF/services/de.focus_shift.jollyday.core.spi.ConfigurationService
new file mode 100644
index 000000000..a7a94289a
--- /dev/null
+++ b/jollyday-pojo/src/main/resources/META-INF/services/de.focus_shift.jollyday.core.spi.ConfigurationService
@@ -0,0 +1 @@
+de.focus_shift.jollyday.pojo.PojoConfigurationService
diff --git a/jollyday-pojo/src/test/java/de/focus_shift/jollyday/pojo/test/PojoConfigurationServiceTest.java b/jollyday-pojo/src/test/java/de/focus_shift/jollyday/pojo/test/PojoConfigurationServiceTest.java
new file mode 100644
index 000000000..08cec928c
--- /dev/null
+++ b/jollyday-pojo/src/test/java/de/focus_shift/jollyday/pojo/test/PojoConfigurationServiceTest.java
@@ -0,0 +1,41 @@
+package de.focus_shift.jollyday.pojo.test;
+
+import java.time.LocalDate;
+import java.time.MonthDay;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import de.focus_shift.jollyday.core.HolidayManager;
+import de.focus_shift.jollyday.core.ManagerParameter;
+import de.focus_shift.jollyday.core.ManagerParameters;
+import de.focus_shift.jollyday.core.spi.Configuration;
+import de.focus_shift.jollyday.core.spi.Limited.YearCycle;
+import de.focus_shift.jollyday.pojo.PojoConfigurationService;
+import de.focus_shift.jollyday.pojo.PojoFixed;
+
+import static de.focus_shift.jollyday.core.HolidayType.OBSERVANCE;
+
+class PojoConfigurationServiceTest {
+
+ @Test
+ void enhancePojoConfigurationServiceWithSpecialHoliday() {
+
+ PojoConfigurationService javaConfigurationService = new PojoConfigurationService();
+ ManagerParameter parameter = ManagerParameters.create("de");
+
+ HolidayManager holidayManager = HolidayManager.getInstance(parameter);
+ Assertions.assertFalse(holidayManager.isHoliday(LocalDate.of(2022, 3, 22)), "Precondition 22.3 should be no holiday");
+
+ // add new holiday for 22.3 dynamically via code/api
+ Configuration configuration = javaConfigurationService.getConfiguration(parameter);
+ PojoFixed stgandulfholiday = new PojoFixed("St.Gandulf's Day", OBSERVANCE, null, null, YearCycle.EVERY_YEAR, null, MonthDay.of(3, 22));
+ configuration.holidays().fixed().add(stgandulfholiday);
+ HolidayManager.clearManagerCache(); // we have to clear the manager cache because otherwise the holiday manager from above with old configuration would be reused.
+
+ holidayManager = HolidayManager.getInstance(parameter);
+ Assertions.assertTrue(holidayManager.isHoliday(LocalDate.of(2022, 3, 22), OBSERVANCE), "Ensure newly added holiday is recognized");
+
+ }
+}
+
diff --git a/jollyday-pojo/src/test/java/module-info.java b/jollyday-pojo/src/test/java/module-info.java
new file mode 100644
index 000000000..7310e21d3
--- /dev/null
+++ b/jollyday-pojo/src/test/java/module-info.java
@@ -0,0 +1,12 @@
+module de.focus_shift.jollyday.pojo.test {
+
+ opens de.focus_shift.jollyday.pojo.test to
+ org.junit.platform.commons;
+
+ requires de.focus_shift.jollyday.core;
+ requires de.focus_shift.jollyday.pojo;
+ requires org.assertj.core;
+ requires org.junit.jupiter.api;
+ requires org.junit.jupiter.params;
+ requires org.threeten.extra;
+}
diff --git a/pom.xml b/pom.xml
index 220654c95..1ef21c2be 100644
--- a/pom.xml
+++ b/pom.xml
@@ -57,6 +57,8 @@
jollyday-core
jollyday-jackson
jollyday-jaxb
+ jollyday-pojo
+ jollyday-pojo-generator