<target name="compile" depends="clean" description="Compiles .java source code to .class bytecode.">
<mkdir dir="build/classes/" />
- <javac target="1.8" source="1.8" srcdir="src/java/" destdir="build/classes/">
+ <javac target="1.8" source="1.8" srcdir="src/main/java/" destdir="build/classes/">
<compilerarg value="-Xlint:none" />
</javac>
</target>
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
- * See LICENSE for details.
- */
-
-package com.marcozanon.macaco;
-
-
-public class MConstants extends MObject {
-
- public static final String MACACO_VERSION = "7.x";
-
- public static final String TEXT_ENCODING = "UTF-8";
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
- * See LICENSE for details.
- */
-
-package com.marcozanon.macaco;
-
-public abstract class MException extends Exception {
-
- /* */
-
- public MException() {
- super();
- }
-
- public MException(String message) {
- super(message);
- }
-
- public MException(Throwable error) {
- super(error);
- }
-
- public MException(String message, Throwable error) {
- super(message, error);
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
- * See LICENSE for details.
- */
-
-package com.marcozanon.macaco;
-
-import java.io.PrintWriter;
-import java.io.StringWriter;
-
-public class MInformation extends MObject {
-
- /* Generic information. */
-
- public static String getMacacoVersion() {
- return MConstants.MACACO_VERSION;
- }
-
- public static String getMacacoFullName() {
- return "Macaco " + MInformation.getMacacoVersion();
- }
-
- public static String getMacacoCopyrightInformation() {
- StringBuilder s = new StringBuilder("");
- s.append(MInformation.getMacacoFullName() + System.getProperty("line.separator"));
- s.append("Copyright (c) 2009-2022 by Marco Zanon." + System.getProperty("line.separator"));
- s.append("Released under MIT license (see LICENSE.MIT.sample for details)." + System.getProperty("line.separator"));
- s.append("Small portions inspired by other projects or web pages. See source code for additional information.");
- return s.toString();
- }
-
- /* Throwables. */
-
- public static String getThrowableAsString(Throwable throwable) {
- if (null == throwable) {
- throw new IllegalArgumentException("Invalid 'throwable': null.");
- }
- //
- StringWriter sw = new StringWriter();
- PrintWriter pw = new PrintWriter(sw);
- throwable.printStackTrace(pw);
- pw.flush();
- sw.flush();
- //
- return sw.toString();
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
- * See LICENSE for details.
- */
-
-package com.marcozanon.macaco;
-
-public abstract class MObject {
-
- /* */
-
- @Override
- protected MObject clone() {
- throw new UnsupportedOperationException("Please provide manually by yourself.");
- }
-
- @Override
- public String toString() {
- throw new UnsupportedOperationException("Please use appropriate methods (if any).");
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
- * See LICENSE for details.
- */
-
-package com.marcozanon.macaco.conversion;
-
-import com.marcozanon.macaco.MException;
-
-public abstract class MConversionException extends MException {
-
- /* */
-
- public MConversionException() {
- super();
- }
-
- public MConversionException(String message) {
- super(message);
- }
-
- public MConversionException(Throwable error) {
- super(error);
- }
-
- public MConversionException(String message, Throwable error) {
- super(message, error);
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
- * See LICENSE for details.
- */
-
-package com.marcozanon.macaco.conversion;
-
-import com.marcozanon.macaco.MObject;
-import com.marcozanon.macaco.text.MText;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.GregorianCalendar;
-import java.util.LinkedHashSet;
-import java.util.Locale;
-import java.util.TimeZone;
-
-public class MDateConverter extends MObject {
-
- protected LinkedHashSet<String> dateFormats = new LinkedHashSet<String>();
- protected Locale locale = null;
- protected TimeZone timeZone = null;
-
- /* */
-
- public MDateConverter(String defaultDateFormat, Locale locale) {
- this(defaultDateFormat, locale, TimeZone.getDefault());
- }
-
- public MDateConverter(String defaultDateFormat, Locale locale, TimeZone timeZone) {
- super();
- //
- this.addDateFormat(defaultDateFormat);
- this.setLocale(locale);
- this.setTimeZone(timeZone);
- }
-
- public MDateConverter(LinkedHashSet<String> dateFormats, Locale locale, TimeZone timeZone) {
- super();
- //
- this.setDateFormats(dateFormats);
- this.setLocale(locale);
- this.setTimeZone(timeZone);
- }
-
- @Override
- public MDateConverter clone() {
- return new MDateConverter(this.getDateFormats(), this.getLocale(), this.getTimeZone());
- }
-
- /* Date formats. */
-
- public void setDateFormats(LinkedHashSet<String> dateFormats) {
- if (null == dateFormats) {
- throw new IllegalArgumentException("Invalid 'dateFormats': null.");
- }
- else if (0 == dateFormats.size()) {
- throw new IllegalArgumentException("Invalid 'dateFormats': empty.");
- }
- else {
- for (String dateFormat: dateFormats) {
- MDateConverter.checkDateFormat(dateFormat);
- }
- }
- //
- synchronized (this.dateFormats) {
- this.dateFormats = dateFormats;
- }
- }
-
- public void addDateFormat(String dateFormat) {
- MDateConverter.checkDateFormat(dateFormat);
- //
- synchronized (this.dateFormats) {
- this.dateFormats.add(dateFormat);
- }
- }
-
- public LinkedHashSet<String> getDateFormats() {
- return this.dateFormats;
- }
-
- public String getDefaultDateFormat() {
- return this.getDateFormats().iterator().next();
- }
-
- /* Locale. */
-
- protected void setLocale(Locale locale) {
- if (null == locale) {
- throw new IllegalArgumentException("Invalid 'locale': null.");
- }
- //
- this.locale = locale;
- }
-
- public Locale getLocale() {
- return this.locale;
- }
-
- /* Time zone. */
-
- protected void setTimeZone(TimeZone timeZone) {
- if (null == timeZone) {
- throw new IllegalArgumentException("Invalid 'timeZone': null.");
- }
- //
- this.timeZone = timeZone;
- }
-
- public TimeZone getTimeZone() {
- return this.timeZone;
- }
-
- /* Conversions. */
-
- protected static void checkDateFormat(String dateFormat) {
- if (MText.isBlank(dateFormat)) {
- throw new IllegalArgumentException("Invalid 'dateFormat': null or empty.");
- }
- //
- try {
- SimpleDateFormat testDateFormat = new SimpleDateFormat(dateFormat);
- }
- catch (IllegalArgumentException exception) {
- throw new IllegalArgumentException(String.format("Invalid 'dateFormat': %s.", dateFormat)); // no need to propagate exception
- }
- }
-
- protected static Date getDateFromStringByParameters(String x, String inputDateFormat, Locale inputLocale, TimeZone inputTimeZone) throws MInvalidConversionFormatException {
- if (MText.isBlank(x)) {
- throw new IllegalArgumentException("Invalid 'x': null or empty.");
- }
- MDateConverter.checkDateFormat(inputDateFormat);
- if (null == inputLocale) {
- throw new IllegalArgumentException("Invalid 'inputLocale': null.");
- }
- if (null == inputTimeZone) {
- throw new IllegalArgumentException("Invalid 'inputTimeZone': null.");
- }
- //
- Calendar c1 = Calendar.getInstance(inputTimeZone, inputLocale);
- c1.clear();
- c1.setLenient(false);
- SimpleDateFormat sdf = new SimpleDateFormat(inputDateFormat, inputLocale);
- sdf.setCalendar(c1);
- Date d1 = null;
- try {
- d1 = sdf.parse(x);
- }
- catch (ParseException exception) {
- throw new MInvalidConversionFormatException(String.format("Invalid 'x' or parsing: %s (input format: %s).", x, inputDateFormat)); // no need to propagate exception
- }
- Calendar c2 = Calendar.getInstance(inputTimeZone, inputLocale);
- c2.clear();
- c2.set(Calendar.YEAR, c1.get(Calendar.YEAR));
- c2.set(Calendar.MONTH, c1.get(Calendar.MONTH));
- c2.set(Calendar.DAY_OF_MONTH, c1.get(Calendar.DAY_OF_MONTH));
- c2.set(Calendar.HOUR_OF_DAY, c1.get(Calendar.HOUR_OF_DAY));
- c2.set(Calendar.MINUTE, c1.get(Calendar.MINUTE));
- c2.set(Calendar.SECOND, c1.get(Calendar.SECOND));
- Date d2 = c2.getTime();
- if (!x.equals(sdf.format(d2))) {
- throw new MInvalidConversionFormatException(String.format("Invalid 'x' or parsing: %s (input format: %s).", x, inputDateFormat));
- }
- return d2;
- }
-
- protected static String getStringFromDateByParameters(Date date, String outputDateFormat, Locale outputLocale, TimeZone outputTimeZone) {
- if (null == date) {
- throw new IllegalArgumentException("Invalid 'date': null.");
- }
- MDateConverter.checkDateFormat(outputDateFormat);
- if (null == outputLocale) {
- throw new IllegalArgumentException("Invalid 'outputLocale': null.");
- }
- if (null == outputTimeZone) {
- throw new IllegalArgumentException("Invalid 'outputTimeZone': null.");
- }
- //
- SimpleDateFormat sdf = new SimpleDateFormat(outputDateFormat, outputLocale);
- sdf.setCalendar(Calendar.getInstance(outputTimeZone, outputLocale));
- return sdf.format(date);
- }
-
- public Date getDateFromString(String x) throws MInvalidConversionFormatException {
- Date y = null;
- for (String dateFormat: this.getDateFormats()) {
- try {
- y = MDateConverter.getDateFromStringByParameters(x, dateFormat, this.getLocale(), this.getTimeZone());
- return y;
- }
- catch (MInvalidConversionFormatException exception) {
- }
- }
- if (null == y) {
- throw new MInvalidConversionFormatException(String.format("Invalid 'x': %s.", x));
- }
- return y; // necessary to avoid Java compilation errors
- }
-
- public String getStringFromDate(Date x) {
- return MDateConverter.getStringFromDateByParameters(x, this.getDefaultDateFormat(), this.getLocale(), this.getTimeZone());
- }
-
- /* Helpers. */
-
- public static Date getFlatDate(Date x) {
- if (null == x) {
- return x;
- }
- //
- Calendar calendar = new GregorianCalendar();
- calendar.setTime(x);
- calendar.set(Calendar.HOUR_OF_DAY, 0);
- calendar.set(Calendar.MINUTE, 0);
- calendar.set(Calendar.SECOND, 0);
- calendar.set(Calendar.MILLISECOND, 0);
- return calendar.getTime();
- }
-
- public static Date getCeilDate(Date x) {
- if (null == x) {
- return x;
- }
- //
- Calendar calendar = new GregorianCalendar();
- calendar.setTime(x);
- calendar.set(Calendar.HOUR_OF_DAY, 23);
- calendar.set(Calendar.MINUTE, 59);
- calendar.set(Calendar.SECOND, 59);
- calendar.set(Calendar.MILLISECOND, 999);
- return calendar.getTime();
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
- * See LICENSE for details.
- */
-
-package com.marcozanon.macaco.conversion;
-
-@SuppressWarnings("serial")
-public class MInvalidConversionFormatException extends MConversionException {
-
- /* */
-
- public MInvalidConversionFormatException() {
- super();
- }
-
- public MInvalidConversionFormatException(String message) {
- super(message);
- }
-
- public MInvalidConversionFormatException(Throwable error) {
- super(error);
- }
-
- public MInvalidConversionFormatException(String message, Throwable error) {
- super(message, error);
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
- * See LICENSE for details.
- */
-
-package com.marcozanon.macaco.conversion;
-
-import com.marcozanon.macaco.MObject;
-import com.marcozanon.macaco.text.MText;
-import java.time.LocalDate;
-import java.time.format.DateTimeFormatter;
-import java.time.format.DateTimeParseException;
-import java.time.format.ResolverStyle;
-import java.util.LinkedHashSet;
-import java.util.Locale;
-
-public class MLocalDateConverter extends MObject {
-
- protected LinkedHashSet<String> dateFormats = new LinkedHashSet<String>();
- protected Locale locale = null;
-
- /* */
-
- public MLocalDateConverter(String defaultDateFormat, Locale locale) {
- super();
- //
- this.addDateFormat(defaultDateFormat);
- this.setLocale(locale);
- }
-
- public MLocalDateConverter(LinkedHashSet<String> dateFormats, Locale locale) {
- super();
- //
- this.setDateFormats(dateFormats);
- this.setLocale(locale);
- }
-
- @Override
- public MLocalDateConverter clone() {
- return new MLocalDateConverter(this.getDateFormats(), this.getLocale());
- }
-
- /* Date formats. */
-
- public void setDateFormats(LinkedHashSet<String> dateFormats) {
- if (null == dateFormats) {
- throw new IllegalArgumentException("Invalid 'dateFormats': null.");
- }
- else if (0 == dateFormats.size()) {
- throw new IllegalArgumentException("Invalid 'dateFormats': empty.");
- }
- else {
- for (String dateFormat: dateFormats) {
- MLocalDateConverter.checkDateFormat(dateFormat);
- }
- }
- //
- synchronized (this.dateFormats) {
- this.dateFormats = dateFormats;
- }
- }
-
- public void addDateFormat(String dateFormat) {
- MLocalDateConverter.checkDateFormat(dateFormat);
- //
- synchronized (this.dateFormats) {
- this.dateFormats.add(dateFormat);
- }
- }
-
- public LinkedHashSet<String> getDateFormats() {
- return this.dateFormats;
- }
-
- public String getDefaultDateFormat() {
- return this.getDateFormats().iterator().next();
- }
-
- /* Locale. */
-
- protected void setLocale(Locale locale) {
- if (null == locale) {
- throw new IllegalArgumentException("Invalid 'locale': null.");
- }
- //
- this.locale = locale;
- }
-
- public Locale getLocale() {
- return this.locale;
- }
-
- /* Conversions. */
-
- protected static void checkDateFormat(String dateFormat) {
- if (MText.isBlank(dateFormat)) {
- throw new IllegalArgumentException("Invalid 'dateFormat': null or empty.");
- }
- //
- try {
- DateTimeFormatter.ofPattern(dateFormat).withResolverStyle(ResolverStyle.STRICT);
- }
- catch (IllegalArgumentException exception) {
- throw new IllegalArgumentException(String.format("Invalid 'dateFormat': %s.", dateFormat)); // no need to propagate exception
- }
- }
-
- protected static LocalDate getDateFromStringByParameters(String x, String inputDateFormat, Locale inputLocale) throws MInvalidConversionFormatException {
- if (MText.isBlank(x)) {
- throw new IllegalArgumentException("Invalid 'x': null or empty.");
- }
- MLocalDateConverter.checkDateFormat(inputDateFormat);
- if (null == inputLocale) {
- throw new IllegalArgumentException("Invalid 'inputLocale': null.");
- }
- //
- LocalDate d = null;
- try {
- d = LocalDate.parse(x, DateTimeFormatter.ofPattern(inputDateFormat, inputLocale).withResolverStyle(ResolverStyle.STRICT));
- }
- catch (DateTimeParseException exception) {
- throw new MInvalidConversionFormatException(String.format("Invalid 'x' or parsing: %s (input format: %s).", x, inputDateFormat)); // no need to propagate exception
- }
- //
- return d;
- }
-
- protected static String getStringFromDateByParameters(LocalDate date, String outputDateFormat, Locale outputLocale) {
- if (null == date) {
- throw new IllegalArgumentException("Invalid 'date': null.");
- }
- MLocalDateConverter.checkDateFormat(outputDateFormat);
- if (null == outputLocale) {
- throw new IllegalArgumentException("Invalid 'outputLocale': null.");
- }
- //
- return date.format(DateTimeFormatter.ofPattern(outputDateFormat, outputLocale).withResolverStyle(ResolverStyle.STRICT));
- }
-
- public LocalDate getDateFromString(String x) throws MInvalidConversionFormatException {
- LocalDate y = null;
- for (String dateFormat: this.getDateFormats()) {
- try {
- y = MLocalDateConverter.getDateFromStringByParameters(x, dateFormat, this.getLocale());
- return y;
- }
- catch (MInvalidConversionFormatException exception) {
- }
- }
- if (null == y) {
- throw new MInvalidConversionFormatException(String.format("Invalid 'x': %s.", x));
- }
- return y; // necessary to avoid Java compilation errors
- }
-
- public String getStringFromDate(LocalDate x) {
- return MLocalDateConverter.getStringFromDateByParameters(x, this.getDefaultDateFormat(), this.getLocale());
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
- * See LICENSE for details.
- */
-
-package com.marcozanon.macaco.conversion;
-
-import com.marcozanon.macaco.MObject;
-import com.marcozanon.macaco.text.MText;
-import java.time.LocalDateTime;
-import java.time.format.DateTimeFormatter;
-import java.time.format.DateTimeParseException;
-import java.time.format.ResolverStyle;
-import java.util.LinkedHashSet;
-import java.util.Locale;
-
-public class MLocalDateTimeConverter extends MObject {
-
- protected LinkedHashSet<String> datetimeFormats = new LinkedHashSet<String>();
- protected Locale locale = null;
-
- /* */
-
- public MLocalDateTimeConverter(String defaultDatetimeFormat, Locale locale) {
- super();
- //
- this.addDatetimeFormat(defaultDatetimeFormat);
- this.setLocale(locale);
- }
-
- public MLocalDateTimeConverter(LinkedHashSet<String> datetimeFormats, Locale locale) {
- super();
- //
- this.setDatetimeFormats(datetimeFormats);
- this.setLocale(locale);
- }
-
- @Override
- public MLocalDateTimeConverter clone() {
- return new MLocalDateTimeConverter(this.getDatetimeFormats(), this.getLocale());
- }
-
- /* Datetime formats. */
-
- public void setDatetimeFormats(LinkedHashSet<String> datetimeFormats) {
- if (null == datetimeFormats) {
- throw new IllegalArgumentException("Invalid 'datetimeFormats': null.");
- }
- else if (0 == datetimeFormats.size()) {
- throw new IllegalArgumentException("Invalid 'datetimeFormats': empty.");
- }
- else {
- for (String datetimeFormat: datetimeFormats) {
- MLocalDateTimeConverter.checkDatetimeFormat(datetimeFormat);
- }
- }
- //
- synchronized (this.datetimeFormats) {
- this.datetimeFormats = datetimeFormats;
- }
- }
-
- public void addDatetimeFormat(String datetimeFormat) {
- MLocalDateTimeConverter.checkDatetimeFormat(datetimeFormat);
- //
- synchronized (this.datetimeFormats) {
- this.datetimeFormats.add(datetimeFormat);
- }
- }
-
- public LinkedHashSet<String> getDatetimeFormats() {
- return this.datetimeFormats;
- }
-
- public String getDefaultDatetimeFormat() {
- return this.getDatetimeFormats().iterator().next();
- }
-
- /* Locale. */
-
- protected void setLocale(Locale locale) {
- if (null == locale) {
- throw new IllegalArgumentException("Invalid 'locale': null.");
- }
- //
- this.locale = locale;
- }
-
- public Locale getLocale() {
- return this.locale;
- }
-
- /* Conversions. */
-
- protected static void checkDatetimeFormat(String datetimeFormat) {
- if (MText.isBlank(datetimeFormat)) {
- throw new IllegalArgumentException("Invalid 'datetimeFormat': null or empty.");
- }
- //
- try {
- DateTimeFormatter.ofPattern(datetimeFormat).withResolverStyle(ResolverStyle.STRICT);
- }
- catch (IllegalArgumentException exception) {
- throw new IllegalArgumentException(String.format("Invalid 'datetimeFormat': %s.", datetimeFormat)); // no need to propagate exception
- }
- }
-
- protected static LocalDateTime getDatetimeFromStringByParameters(String x, String inputDatetimeFormat, Locale inputLocale) throws MInvalidConversionFormatException {
- if (MText.isBlank(x)) {
- throw new IllegalArgumentException("Invalid 'x': null or empty.");
- }
- MLocalDateTimeConverter.checkDatetimeFormat(inputDatetimeFormat);
- if (null == inputLocale) {
- throw new IllegalArgumentException("Invalid 'inputLocale': null.");
- }
- //
- LocalDateTime dt = null;
- try {
- dt = LocalDateTime.parse(x, DateTimeFormatter.ofPattern(inputDatetimeFormat, inputLocale).withResolverStyle(ResolverStyle.STRICT));
- }
- catch (DateTimeParseException exception) {
- throw new MInvalidConversionFormatException(String.format("Invalid 'x' or parsing: %s (input format: %s).", x, inputDatetimeFormat)); // no need to propagate exception
- }
- //
- return dt;
- }
-
- protected static String getStringFromDatetimeByParameters(LocalDateTime datetime, String outputDatetimeFormat, Locale outputLocale) {
- if (null == datetime) {
- throw new IllegalArgumentException("Invalid 'datetime': null.");
- }
- MLocalDateTimeConverter.checkDatetimeFormat(outputDatetimeFormat);
- if (null == outputLocale) {
- throw new IllegalArgumentException("Invalid 'outputLocale': null.");
- }
- //
- return datetime.format(DateTimeFormatter.ofPattern(outputDatetimeFormat, outputLocale).withResolverStyle(ResolverStyle.STRICT));
- }
-
- public LocalDateTime getDatetimeFromString(String x) throws MInvalidConversionFormatException {
- LocalDateTime y = null;
- for (String datetimeFormat: this.getDatetimeFormats()) {
- try {
- y = MLocalDateTimeConverter.getDatetimeFromStringByParameters(x, datetimeFormat, this.getLocale());
- return y;
- }
- catch (MInvalidConversionFormatException exception) {
- }
- }
- if (null == y) {
- throw new MInvalidConversionFormatException(String.format("Invalid 'x': %s.", x));
- }
- return y; // necessary to avoid Java compilation errors
- }
-
- public String getStringFromDatetime(LocalDateTime x) {
- return MLocalDateTimeConverter.getStringFromDatetimeByParameters(x, this.getDefaultDatetimeFormat(), this.getLocale());
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
- * See LICENSE for details.
- */
-
-package com.marcozanon.macaco.conversion;
-
-import com.marcozanon.macaco.MObject;
-import com.marcozanon.macaco.text.MText;
-import java.math.BigDecimal;
-import java.math.RoundingMode;
-import java.text.DecimalFormat;
-import java.text.DecimalFormatSymbols;
-import java.text.ParsePosition;
-import java.util.LinkedHashSet;
-import java.util.Locale;
-
-public class MNumberConverter extends MObject {
-
- protected LinkedHashSet<String> numberFormats = new LinkedHashSet<String>();
- protected Locale locale = null;
-
- /* */
-
- public MNumberConverter(String defaultNumberFormat, Locale locale) {
- super();
- //
- this.addNumberFormat(defaultNumberFormat);
- this.setLocale(locale);
- }
-
- public MNumberConverter(LinkedHashSet<String> numberFormats, Locale locale) {
- super();
- //
- this.setNumberFormats(numberFormats);
- this.setLocale(locale);
- }
-
- @Override
- public MNumberConverter clone() {
- return new MNumberConverter(this.getNumberFormats(), this.getLocale());
- }
-
- /* Number formats. */
-
- public void setNumberFormats(LinkedHashSet<String> numberFormats) {
- if (null == numberFormats) {
- throw new IllegalArgumentException("Invalid 'numberFormats': null.");
- }
- else if (0 == numberFormats.size()) {
- throw new IllegalArgumentException("Invalid 'numberFormats': empty.");
- }
- else {
- for (String numberFormat: numberFormats) {
- MNumberConverter.checkNumberFormat(numberFormat);
- }
- }
- //
- synchronized (this.numberFormats) {
- this.numberFormats = numberFormats;
- }
- }
-
- public void addNumberFormat(String numberFormat) {
- MNumberConverter.checkNumberFormat(numberFormat);
- //
- synchronized (this.numberFormats) {
- this.numberFormats.add(numberFormat);
- }
- }
-
- public LinkedHashSet<String> getNumberFormats() {
- return this.numberFormats;
- }
-
- public String getDefaultNumberFormat() {
- return this.getNumberFormats().iterator().next();
- }
-
- /* Locale. */
-
- protected void setLocale(Locale locale) {
- if (null == locale) {
- throw new IllegalArgumentException("Invalid 'locale': null.");
- }
- //
- this.locale = locale;
- }
-
- public Locale getLocale() {
- return this.locale;
- }
-
- /* Conversions. */
-
- protected static void checkNumberFormat(String numberFormat) {
- if (MText.isBlank(numberFormat)) {
- throw new IllegalArgumentException("Invalid 'numberFormat': null or empty.");
- }
- //
- try {
- DecimalFormat testNumberFormat = new DecimalFormat(numberFormat);
- }
- catch (IllegalArgumentException exception) {
- throw new IllegalArgumentException(String.format("Invalid 'numberFormat': %s.", numberFormat)); // no need to propagate exception
- }
- }
-
- protected static BigDecimal getNumberFromStringByParameters(String x, String inputNumberFormat, Locale inputLocale) throws MInvalidConversionFormatException {
- if (MText.isBlank(x)) {
- throw new IllegalArgumentException("Invalid 'x': null or empty.");
- }
- MNumberConverter.checkNumberFormat(inputNumberFormat);
- if (null == inputLocale) {
- throw new IllegalArgumentException("Invalid 'inputLocale': null.");
- }
- //
- DecimalFormatSymbols dfs = new DecimalFormatSymbols(inputLocale);
- DecimalFormat df = new DecimalFormat(inputNumberFormat, dfs);
- df.setParseBigDecimal(true);
- ParsePosition validPosition = new ParsePosition(0);
- BigDecimal bd = (BigDecimal)df.parse(x, validPosition);
- if (validPosition.getIndex() < x.length()) {
- throw new MInvalidConversionFormatException(String.format("Invalid 'x' or parsing: %s (input format: %s).", x, inputNumberFormat));
- }
- return bd;
- }
-
- protected static String getStringFromNumberByParameters(BigDecimal number, String outputNumberFormat, Locale outputLocale) {
- if (null == number) {
- throw new IllegalArgumentException("Invalid 'number': null.");
- }
- MNumberConverter.checkNumberFormat(outputNumberFormat);
- if (null == outputLocale) {
- throw new IllegalArgumentException("Invalid 'outputLocale': null.");
- }
- //
- DecimalFormatSymbols dfs = new DecimalFormatSymbols(outputLocale);
- DecimalFormat df = new DecimalFormat(outputNumberFormat, dfs);
- df.setRoundingMode(RoundingMode.HALF_UP);
- return df.format(number);
- }
-
- public BigDecimal getNumberFromString(String x) throws MInvalidConversionFormatException {
- BigDecimal y = null;
- for (String numberFormat: this.getNumberFormats()) {
- try {
- y = MNumberConverter.getNumberFromStringByParameters(x, numberFormat, this.getLocale());
- return y;
- }
- catch (MInvalidConversionFormatException exception) {
- }
- }
- if (null == y) {
- throw new MInvalidConversionFormatException(String.format("Invalid 'x': %s.", x));
- }
- return y; // necessary to avoid Java compilation errors
- }
-
- public String getStringFromNumber(BigDecimal x) {
- return MNumberConverter.getStringFromNumberByParameters(x, this.getDefaultNumberFormat(), this.getLocale());
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
- * See LICENSE for details.
- */
-
-package com.marcozanon.macaco.database;
-
-import com.marcozanon.macaco.MObject;
-import com.marcozanon.macaco.logging.MLogListener;
-import com.marcozanon.macaco.text.MText;
-import java.sql.Connection;
-import java.sql.DriverManager;
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
-import java.util.LinkedHashMap;
-import java.util.LinkedList;
-
-public class MDatabaseConnection extends MObject {
-
- public static enum TransactionStatus {
- CLOSED,
- SUCCESSFUL,
- FAILED
- };
-
- protected String driver = null;
- protected String url = null;
- protected String username = null;
- protected String password = null;
- protected boolean localTypesMode = false;
- protected MLogListener logListener = null;
-
- protected Connection connection = null;
-
- protected MDatabaseConnection.TransactionStatus transactionStatus = MDatabaseConnection.TransactionStatus.CLOSED;
-
- /* */
-
- public MDatabaseConnection(String driver, String url, String username, String password, boolean localTypesMode, MLogListener logListener) throws MDatabaseConnectionFailureException {
- super();
- //
- if (MText.isBlank(driver)) {
- throw new IllegalArgumentException("Invalid 'driver': null or empty.");
- }
- if (MText.isBlank(url)) {
- throw new IllegalArgumentException("Invalid 'url': null or empty.");
- }
- if (null == username) {
- throw new IllegalArgumentException("Invalid 'username': null.");
- }
- if (null == password) {
- throw new IllegalArgumentException("Invalid 'password': null.");
- }
- //
- this.driver = driver;
- this.url = url;
- this.username = username;
- this.password = password;
- this.localTypesMode = localTypesMode;
- this.setLogListener(logListener);
- // Load driver.
- try {
- Class.forName(this.getDriver()).newInstance();
- }
- catch (ClassNotFoundException exception) {
- throw new MDatabaseConnectionFailureException("Could not load driver.", exception);
- }
- catch (IllegalAccessException exception) {
- throw new MDatabaseConnectionFailureException("Could not load driver.", exception);
- }
- catch (InstantiationException exception) {
- throw new MDatabaseConnectionFailureException("Could not load driver.", exception);
- }
- // Initialize the connection.
- this.initialize();
- }
-
- public void initialize() throws MDatabaseConnectionFailureException {
- // Establish a connection.
- try {
- this.connection = DriverManager.getConnection(this.getUrl(), this.getUsername(), this.getPassword());
- }
- catch (SQLException exception) {
- throw new MDatabaseConnectionFailureException("Could not get database connection.", exception);
- }
- // Set SQL mode to standard ANSI and TRADITIONAL.
- try {
- this.getConnection().createStatement().executeUpdate("SET SQL_MODE = 'ANSI,TRADITIONAL'");
-/* Disabled to prevent an infinite loop.
- this.logStatement("### SET SQL_MODE = 'ANSI,TRADITIONAL' ###");
-*/
- }
- catch (SQLException exception) {
- throw new MDatabaseConnectionFailureException("Could not set SQL mode to ANSI and TRADITIONAL.", exception);
- }
- }
-
- protected void check() throws MDatabaseConnectionFailureException {
- try {
- this.getConnection().createStatement().executeQuery("/* ping */ SELECT 1");
- }
- catch (SQLException exception) {
- this.initialize();
- }
- }
-
- public void close() throws MDatabaseConnectionFailureException {
- try {
- this.getConnection().close();
- }
- catch (SQLException exception) {
- throw new MDatabaseConnectionFailureException("Could not close database connection.", exception);
- }
- }
-
- public boolean isClosed() throws MDatabaseConnectionFailureException {
- try {
- return this.getConnection().isClosed();
- }
- catch (SQLException exception) {
- throw new MDatabaseConnectionFailureException("Could not determine the state.", exception);
- }
- }
-
- @Override
- protected void finalize() {
- try {
- this.close();
- }
- catch (Exception exception) {
- }
- }
-
- /* Driver. */
-
- public String getDriver() {
- return this.driver;
- }
-
- /* Url. */
-
- public String getUrl() {
- return this.url;
- }
-
- /* Username. */
-
- public String getUsername() {
- return this.username;
- }
-
- /* Password. */
-
- public String getPassword() {
- return this.password;
- }
-
- /* Local types mode. */
-
- public boolean getLocalTypesMode() {
- return this.localTypesMode;
- }
-
- /* Connection. */
-
- protected Connection getConnection() {
- return this.connection;
- }
-
- /* Transactions. */
-
- protected void setTransactionStatus(MDatabaseConnection.TransactionStatus transactionStatus) {
- if (null == transactionStatus) {
- throw new IllegalArgumentException("Invalid 'transactionStatus': null.");
- }
- //
- this.transactionStatus = transactionStatus;
- }
-
- public MDatabaseConnection.TransactionStatus getTransactionStatus() {
- return this.transactionStatus;
- }
-
- public void startTransaction() throws MSqlTransactionException {
- if (MDatabaseConnection.TransactionStatus.CLOSED != this.getTransactionStatus()) {
- throw new MSqlTransactionException("Nested transactions not allowed.");
- }
- //
- try {
- // Check the connection.
- this.check();
- // Start the transaction.
- this.getConnection().setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
- this.getConnection().setAutoCommit(false);
- this.setTransactionStatus(MDatabaseConnection.TransactionStatus.SUCCESSFUL);
- this.logStatement("### BEGIN TRANSACTION ###");
- }
- catch (MDatabaseConnectionFailureException exception) {
- throw new MSqlTransactionException("Could not start transaction.", exception);
- }
- catch (SQLException exception) {
- throw new MSqlTransactionException("Could not start transaction.", exception);
- }
- }
-
- public void rollBackTransaction() throws MSqlTransactionException {
- if (MDatabaseConnection.TransactionStatus.CLOSED == this.getTransactionStatus()) {
- throw new MSqlTransactionException("Transaction not started.");
- }
- //
- try {
-/*
- // Check the connection.
- this.check();
-*/
- // Cancel the transaction.
- this.getConnection().rollback();
- this.getConnection().setAutoCommit(true);
- this.setTransactionStatus(MDatabaseConnection.TransactionStatus.CLOSED);
- this.logStatement("### ROLLBACK ###");
- }
- catch (SQLException exception) {
- this.setTransactionStatus(MDatabaseConnection.TransactionStatus.FAILED);
- throw new MSqlTransactionException("Could not roll back transaction.", exception);
- }
- }
-
- public MDatabaseConnection.TransactionStatus commitTransaction() throws MSqlTransactionException {
- switch (this.getTransactionStatus()) {
- case SUCCESSFUL:
- try {
-/*
- // Check the connection.
- this.check();
-*/
- // Commit the transaction.
- this.getConnection().commit();
- this.getConnection().setAutoCommit(true);
- this.setTransactionStatus(MDatabaseConnection.TransactionStatus.CLOSED);
- this.logStatement("### COMMIT ###");
- return MDatabaseConnection.TransactionStatus.SUCCESSFUL;
- }
- catch (SQLException exception) {
- this.rollBackTransaction();
- return MDatabaseConnection.TransactionStatus.FAILED;
- }
- case FAILED:
- this.rollBackTransaction();
- return MDatabaseConnection.TransactionStatus.FAILED;
- default: // instead of "case CLOSED:" (necessary to avoid Java compilation errors)
- throw new MSqlTransactionException("Transaction not started.");
- }
- }
-
- /* Statements. */
-
- public MSqlStatementResults executePreparedStatement(String statement) throws MDatabaseConnectionFailureException, MSqlStatementException {
- return this.executePreparedStatement(statement, new LinkedList<Object>());
- }
-
- public MSqlStatementResults executePreparedStatement(String statement, LinkedList<Object> parameters) throws MDatabaseConnectionFailureException, MSqlStatementException {
- return this.executePreparedStatement(statement, parameters, this.getLocalTypesMode());
- }
-
- public MSqlStatementResults executePreparedStatement(String statement, LinkedList<Object> parameters, boolean localTypesMode) throws MDatabaseConnectionFailureException, MSqlStatementException {
- return this.executePreparedStatement(statement, parameters, this.getLocalTypesMode(), true);
- };
-
- public MSqlStatementResults executePreparedStatement(String statement, LinkedList<Object> parameters, boolean localTypesMode, boolean loggableStatement) throws MDatabaseConnectionFailureException, MSqlStatementException {
- if (MText.isBlank(statement)) {
- throw new IllegalArgumentException("Invalid 'statement': null or empty.");
- }
- if (null == parameters) {
- throw new IllegalArgumentException("Invalid 'parameters': null.");
- }
- //
- MSqlStatementResults results = null;
- PreparedStatement preparedStatement = null;
- try {
- // Check the connection.
- this.check();
- // Prepare the statement.
- preparedStatement = this.getConnection().prepareStatement(statement, PreparedStatement.RETURN_GENERATED_KEYS);
- for (int p = 0; parameters.size() > p; p++) {
- preparedStatement.setObject(p + 1, parameters.get(p));
- }
- // Execute the statement and parse the results.
- preparedStatement.execute();
- results = new MSqlStatementResults(preparedStatement, localTypesMode);
- if (loggableStatement) {
- this.logStatement(preparedStatement.toString());
- }
- }
- catch (MDatabaseConnectionFailureException exception) {
- if (MDatabaseConnection.TransactionStatus.SUCCESSFUL == this.getTransactionStatus()) {
- this.setTransactionStatus(MDatabaseConnection.TransactionStatus.FAILED);
- }
- throw new MSqlStatementException(String.format("Could not execute prepared statement: %s.", preparedStatement), exception);
- }
- catch (SQLException exception) {
- if (MDatabaseConnection.TransactionStatus.SUCCESSFUL == this.getTransactionStatus()) {
- this.setTransactionStatus(MDatabaseConnection.TransactionStatus.FAILED);
- }
- throw new MSqlStatementException(String.format("Could not execute prepared statement: %s.", preparedStatement), exception);
- }
- return results;
- }
-
- /* Tables. */
-
- public MSqlTable getTable(String table, String primaryKey) {
- return new MSqlTable(this, table, primaryKey);
- }
-
- /* Logging. */
-
- public void setLogListener(MLogListener logListener) {
- this.logListener = logListener;
- }
-
- protected MLogListener getLogListener() {
- return this.logListener;
- }
-
- protected void logStatement(String statement) {
- MLogListener logListener = this.getLogListener();
- if (null != logListener) {
- logListener.onMessageLogging(statement);
- }
- }
-
- /* Engine version. */
-
- public String getEngineVersion() throws MDatabaseConnectionFailureException, MSqlStatementException {
- MSqlStatementResults results = this.executePreparedStatement("SELECT VERSION()");
- LinkedList<LinkedHashMap<String, Object>> resultList = results.getRecords();
- //
- return (String)resultList.get(0).get("VERSION()");
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
- * See LICENSE for details.
- */
-
-package com.marcozanon.macaco.database;
-
-@SuppressWarnings("serial")
-public class MDatabaseConnectionFailureException extends MDatabaseException {
-
- /* */
-
- public MDatabaseConnectionFailureException() {
- super();
- }
-
- public MDatabaseConnectionFailureException(String message) {
- super(message);
- }
-
- public MDatabaseConnectionFailureException(Throwable error) {
- super(error);
- }
-
- public MDatabaseConnectionFailureException(String message, Throwable error) {
- super(message, error);
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
- * See LICENSE for details.
- */
-
-package com.marcozanon.macaco.database;
-
-import com.marcozanon.macaco.MObject;
-import com.marcozanon.macaco.logging.MLogListener;
-import com.marcozanon.macaco.text.MText;
-
-public class MDatabaseConnectionGenerator extends MObject {
-
- protected String driver = null;
- protected String url = null;
- protected String username = null;
- protected String password = null;
- protected boolean localTypesMode = false;
- protected MLogListener logListener = null;
-
- /* */
-
- public MDatabaseConnectionGenerator(String driver, String url, String username, String password, boolean localTypesMode, MLogListener logListener) {
- super();
- //
- if (MText.isBlank(driver)) {
- throw new IllegalArgumentException("Invalid 'driver': null or empty.");
- }
- if (MText.isBlank(url)) {
- throw new IllegalArgumentException("Invalid 'url': null or empty.");
- }
- if (null == username) {
- throw new IllegalArgumentException("Invalid 'username': null.");
- }
- if (null == password) {
- throw new IllegalArgumentException("Invalid 'password': null.");
- }
- //
- this.driver = driver;
- this.url = url;
- this.username = username;
- this.password = password;
- this.localTypesMode = localTypesMode;
- this.setLogListener(logListener);
- }
-
- /* Driver. */
-
- public String getDriver() {
- return this.driver;
- }
-
- /* Url. */
-
- public String getUrl() {
- return this.url;
- }
-
- /* Username. */
-
- public String getUsername() {
- return this.username;
- }
-
- /* Password. */
-
- public String getPassword() {
- return this.password;
- }
-
- /* Local types mode. */
-
- public boolean getLocalTypesMode() {
- return this.localTypesMode;
- }
-
- /* Log listener. */
-
- public MLogListener getLogListener() {
- return this.logListener;
- }
-
- public void setLogListener(MLogListener logListener) {
- this.logListener = logListener;
- }
-
- /* Generator. */
-
- public MDatabaseConnection getNewDatabaseConnection() throws MDatabaseConnectionFailureException {
- return new MDatabaseConnection(this.getDriver(), this.getUrl(), this.getUsername(), this.getPassword(), this.getLocalTypesMode(), this.getLogListener());
- }
-
- public boolean isGeneratorFor(MDatabaseConnection databaseConnection) {
- if (null == databaseConnection) {
- throw new IllegalArgumentException("Invalid 'databaseConnection': null.");
- }
- //
- if (!databaseConnection.getDriver().equals(this.getDriver())) {
- return false;
- }
- if (!databaseConnection.getUrl().equals(this.getUrl())) {
- return false;
- }
- if (!databaseConnection.getUsername().equals(this.getUsername())) {
- return false;
- }
- if (!databaseConnection.getPassword().equals(this.getPassword())) {
- return false;
- }
- if (databaseConnection.getLocalTypesMode() != this.getLocalTypesMode()) {
- return false;
- }
- if (databaseConnection.getLogListener() != this.getLogListener()) {
- return false;
- }
- //
- return true;
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
- * See LICENSE for details.
- */
-
-package com.marcozanon.macaco.database;
-
-import com.marcozanon.macaco.MObject;
-import com.marcozanon.macaco.logging.MLogListener;
-import java.util.LinkedList;
-
-public class MDatabaseConnectionPool extends MObject {
-
- protected MDatabaseConnectionGenerator databaseConnectionGenerator = null;
-
- protected LinkedList<MDatabaseConnection> databaseConnections = new LinkedList<MDatabaseConnection>();
- protected int minimumSize = 0;
- protected int maximumSize = 0;
-
- /* */
-
- public MDatabaseConnectionPool(String driver, String url, String username, String password, boolean localTypesMode, int minimumSize, int maximumSize, MLogListener logListener) throws MDatabaseConnectionFailureException {
- super();
- //
- if (0 > minimumSize) {
- throw new IllegalArgumentException(String.format("Invalid 'minimumSize': %s.", minimumSize));
- }
- if (1 > maximumSize) {
- throw new IllegalArgumentException(String.format("Invalid 'maximumSize': %s.", maximumSize));
- }
- else if (minimumSize > maximumSize) {
- throw new IllegalArgumentException(String.format("Invalid 'maximumSize': %s < %s ('minimumSize').", maximumSize, minimumSize));
- }
- //
- this.databaseConnectionGenerator = new MDatabaseConnectionGenerator(driver, url, username, password, localTypesMode, logListener);
- this.minimumSize = minimumSize;
- this.maximumSize = maximumSize;
- //
- this.initialize();
- }
-
- protected void initialize() throws MDatabaseConnectionFailureException {
- for (int c = 0; c < this.getMinimumSize(); c++) {
- MDatabaseConnection databaseConnection = this.getDatabaseConnectionGenerator().getNewDatabaseConnection();
- this.pushDatabaseConnection(databaseConnection);
- }
- }
-
- protected void closeConnections() {
- LinkedList<MDatabaseConnection> databaseConnections = this.getDatabaseConnections();
- while (0 < databaseConnections.size()) {
- MDatabaseConnection databaseConnection = databaseConnections.removeLast();
- //
- try {
- databaseConnection.close();
- }
- catch (MDatabaseConnectionFailureException exception) {
- }
- }
- }
-
- @Override
- protected void finalize() {
- this.closeConnections();
- }
-
- /* Database connection generator. */
-
- protected MDatabaseConnectionGenerator getDatabaseConnectionGenerator() {
- return this.databaseConnectionGenerator;
- }
-
- /* Database connections. */
-
- protected LinkedList<MDatabaseConnection> getDatabaseConnections() {
- return this.databaseConnections;
- }
-
- protected int getMinimumSize() {
- return this.minimumSize;
- }
-
- protected int getMaximumSize() {
- return this.maximumSize;
- }
-
- public synchronized MDatabaseConnection popDatabaseConnection() throws MDatabaseConnectionFailureException {
- LinkedList<MDatabaseConnection> databaseConnections = this.getDatabaseConnections();
- MDatabaseConnection databaseConnection = null;
- if (0 == databaseConnections.size()) {
- databaseConnection = this.getDatabaseConnectionGenerator().getNewDatabaseConnection();
- }
- else {
- databaseConnection = databaseConnections.removeLast();
- }
- if (this.getMinimumSize() > databaseConnections.size()) {
- databaseConnections.add(this.getDatabaseConnectionGenerator().getNewDatabaseConnection());
- }
- return databaseConnection;
- }
-
- public synchronized void pushDatabaseConnection(MDatabaseConnection databaseConnection) throws MDatabaseConnectionFailureException {
- if (null == databaseConnection) {
- throw new IllegalArgumentException("Invalid 'databaseConnection': null.");
- }
- else if (databaseConnection.isClosed()) {
- throw new IllegalArgumentException("Invalid 'databaseConnection': closed.");
- }
- //
- if (!this.getDatabaseConnectionGenerator().isGeneratorFor(databaseConnection)) {
- databaseConnection.close();
- }
- else {
- LinkedList<MDatabaseConnection> databaseConnections = this.getDatabaseConnections();
- if (this.getMaximumSize() >= databaseConnections.size()) {
- databaseConnections.add(databaseConnection);
- }
- }
- }
-
- /* Logging. */
-
- public synchronized void setLogListener(MLogListener logListener) {
- this.getDatabaseConnectionGenerator().setLogListener(logListener);
- //
- for (MDatabaseConnection databaseConnection: this.getDatabaseConnections()) {
- databaseConnection.setLogListener(logListener);
- }
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
- * See LICENSE for details.
- */
-
-package com.marcozanon.macaco.database;
-
-import com.marcozanon.macaco.MException;
-
-public abstract class MDatabaseException extends MException {
-
- /* */
-
- public MDatabaseException() {
- super();
- }
-
- public MDatabaseException(String message) {
- super(message);
- }
-
- public MDatabaseException(Throwable error) {
- super(error);
- }
-
- public MDatabaseException(String message, Throwable error) {
- super(message, error);
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
- * See LICENSE for details.
- */
-
-package com.marcozanon.macaco.database;
-
-@SuppressWarnings("serial")
-public class MSqlStatementException extends MDatabaseException {
-
- /* */
-
- public MSqlStatementException() {
- super();
- }
-
- public MSqlStatementException(String message) {
- super(message);
- }
-
- public MSqlStatementException(Throwable error) {
- super(error);
- }
-
- public MSqlStatementException(String message, Throwable error) {
- super(message, error);
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
- * See LICENSE for details.
- */
-
-package com.marcozanon.macaco.database;
-
-import com.marcozanon.macaco.MObject;
-import com.marcozanon.macaco.text.MText;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.ResultSetMetaData;
-import java.sql.SQLException;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.LinkedList;
-
-public class MSqlStatementResults extends MObject {
-
- protected PreparedStatement preparedStatement = null;
- protected boolean localTypesMode = false;
-
- protected ResultSet resultSet = null;
-
- protected LinkedList<LinkedHashMap<String, Object>> records = new LinkedList<LinkedHashMap<String, Object>>();
-
- protected LinkedHashSet<Object> generatedKeys = new LinkedHashSet<Object>();
-
- /* */
-
- public MSqlStatementResults(PreparedStatement preparedStatement, boolean localTypesMode) throws MSqlStatementException {
- super();
- //
- if (null == preparedStatement) {
- throw new IllegalArgumentException("Invalid 'preparedStatement': null.");
- }
- //
- this.preparedStatement = preparedStatement;
- this.localTypesMode = localTypesMode;
- //
- try {
- this.resultSet = this.getPreparedStatement().getResultSet();
- if (null != this.getResultSet()) {
- while (this.getResultSet().next()) {
- ResultSetMetaData resultSetMetaData = this.getResultSet().getMetaData();
- LinkedHashMap<String, Object> record = new LinkedHashMap<String, Object>();
- //
- for (int f = 0; resultSetMetaData.getColumnCount() > f; f++) {
- String field = resultSetMetaData.getColumnLabel(f + 1);
- Object value = this.getResultSet().getObject(field);
- //
- if ((value instanceof java.sql.Date) && (this.getLocalTypesMode())) {
- value = ((java.sql.Date)value).toLocalDate();
- }
- else if ((value instanceof java.sql.Time) && (this.getLocalTypesMode())) {
- value = ((java.sql.Time)value).toLocalTime();
- }
- else if ((value instanceof java.sql.Timestamp) && (this.getLocalTypesMode())) {
- value = ((java.sql.Timestamp)value).toLocalDateTime();
- }
- //
- record.put(field, value);
- }
- //
- this.getRecords().add(record);
- }
- }
- }
- catch (SQLException exception) {
- throw new MSqlStatementException("Could not retrieve statement records.", exception);
- }
- //
- try {
- ResultSet generatedKeysResultSet = this.getPreparedStatement().getGeneratedKeys();
- while (generatedKeysResultSet.next()) {
- this.getGeneratedKeys().add(generatedKeysResultSet.getObject(1));
- }
- generatedKeysResultSet.close();
- }
- catch (SQLException exception) {
- throw new MSqlStatementException("Could not retrieve generated keys.", exception);
- }
- }
-
- public void close() throws MDatabaseConnectionFailureException {
- try {
- if (null != this.getResultSet()) {
- this.getResultSet().close();
- }
- this.getPreparedStatement().close();
- }
- catch (SQLException exception) {
- throw new MDatabaseConnectionFailureException("Could not close statement and/or result set references.", exception);
- }
- }
-
- @Override
- protected void finalize() {
- try {
- this.close();
- }
- catch (Exception exception) {
- }
- }
-
- /* References. */
-
- protected PreparedStatement getPreparedStatement() {
- return this.preparedStatement;
- }
-
- public ResultSet getResultSet() {
- return this.resultSet;
- }
-
- public boolean getLocalTypesMode() {
- return this.localTypesMode;
- }
-
- /* Records. */
-
- public LinkedList<LinkedHashMap<String, Object>> getRecords() {
- return this.records;
- }
-
- public LinkedList<Object> getRecordsByField(String field) {
- if (MText.isBlank(field)) {
- throw new IllegalArgumentException("Invalid 'field': null or empty.");
- }
- //
- LinkedList<Object> recordsByField = new LinkedList<Object>();
- for (LinkedHashMap<String, Object> record: this.getRecords()) {
- Object r = record.get(field);
- if (null == r) {
- throw new IllegalArgumentException(String.format("Invalid 'field': %s.", field));
- }
- recordsByField.add(r);
- }
- return recordsByField;
- }
-
- /* Generated keys. */
-
- public LinkedHashSet<Object> getGeneratedKeys() {
- return this.generatedKeys;
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
- * See LICENSE for details.
- */
-
-package com.marcozanon.macaco.database;
-
-import com.marcozanon.macaco.MObject;
-import com.marcozanon.macaco.text.MText;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.LinkedList;
-import java.util.StringJoiner;
-
-public class MSqlTable extends MObject {
-
- protected MDatabaseConnection databaseConnection = null;
- protected String table = null;
- protected String primaryKey = null;
-
- /* */
-
- public MSqlTable(MDatabaseConnection databaseConnection, String table, String primaryKey) {
- super();
- //
- if (null == databaseConnection) {
- throw new IllegalArgumentException("Invalid 'databaseConnection': null.");
- }
- if (MText.isBlank(table)) {
- throw new IllegalArgumentException("Invalid 'table': null or empty.");
- }
- if (MText.isBlank(primaryKey)) {
- throw new IllegalArgumentException("Invalid 'primaryKey': null or empty.");
- }
- //
- this.databaseConnection = databaseConnection;
- this.table = table;
- this.primaryKey = primaryKey;
- }
-
- /* Database connection. */
-
- protected MDatabaseConnection getDatabaseConnection() {
- return this.databaseConnection;
- }
-
- /* Table. */
-
- public String getTable() {
- return this.table;
- }
-
- /* Primary key. */
-
- public String getPrimaryKey() {
- return this.primaryKey;
- }
-
- /* Statements. */
-
- protected MSqlStatementResults insertRecord(LinkedHashMap<String, Object> map, boolean loggableStatement) throws MDatabaseConnectionFailureException, MSqlStatementException {
- if (null == map) {
- throw new IllegalArgumentException("Invalid 'map': null.");
- }
- //
- LinkedList<Object> p = new LinkedList<Object>();
- StringBuilder fields = new StringBuilder("");
- StringBuilder s = new StringBuilder("");
- for (String field: map.keySet()) {
- fields.append("\"" + this.getTable() + "\".\"" + field + "\", ");
- s.append("?, ");
- p.add(map.get(field));
- }
- fields.delete(fields.length() - 2, fields.length());
- s.delete(s.length() - 2, s.length());
- //
- return this.getDatabaseConnection().executePreparedStatement(" INSERT INTO \"" + this.getTable() + "\""
- + " (" + fields + ")"
- + " VALUES (" + s + ")",
- p, true, loggableStatement);
- }
-
- protected MSqlStatementResults updateRecord(LinkedHashMap<String, Object> map, Object id, boolean loggableStatement) throws MDatabaseConnectionFailureException, MSqlStatementException {
- if (null == map) {
- throw new IllegalArgumentException("Invalid 'map': null.");
- }
- if (null == id) {
- throw new IllegalArgumentException("Invalid 'id': null.");
- }
- //
- LinkedList<Object> p = new LinkedList<Object>();
- StringBuilder s = new StringBuilder("");
- for (String field: map.keySet()) {
- s.append("\"" + this.getTable() + "\".\"" + field + "\" = ?, ");
- p.add(map.get(field));
- }
- s.delete(s.length() - 2, s.length());
- p.add(id);
- //
- return this.getDatabaseConnection().executePreparedStatement(" UPDATE \"" + this.getTable() + "\""
- + " SET " + s
- + " WHERE (\"" + this.getTable() + "\".\"" + this.getPrimaryKey() + "\" = ?)",
- p, true, loggableStatement);
- }
-
- public MSqlStatementResults setRecord(LinkedHashMap<String, Object> map, Object id) throws MDatabaseConnectionFailureException, MSqlStatementException {
- return this.setRecord(map, id, true);
- }
-
- public MSqlStatementResults setRecord(LinkedHashMap<String, Object> map, Object id, boolean loggableStatement) throws MDatabaseConnectionFailureException, MSqlStatementException {
- if (null == id) {
- return this.insertRecord(map, loggableStatement);
- }
- else {
- return this.updateRecord(map, id, loggableStatement);
- }
- }
-
- public MSqlStatementResults getRecord(Object id) throws MDatabaseConnectionFailureException, MSqlStatementException {
- if (null == id) {
- throw new IllegalArgumentException("Invalid 'id': null.");
- }
- //
- LinkedList<Object> p = new LinkedList<Object>();
- p.add(id);
- //
- return this.getDatabaseConnection().executePreparedStatement(" SELECT *"
- + " FROM \"" + this.getTable() + "\""
- + " WHERE (\"" + this.getTable() + "\".\"" + this.getPrimaryKey() + "\" = ?)",
- p);
- }
-
- public MSqlStatementResults deleteRecord(Object id) throws MDatabaseConnectionFailureException, MSqlStatementException {
- if (null == id) {
- throw new IllegalArgumentException("Invalid 'id': null.");
- }
- //
- LinkedList<Object> p = new LinkedList<Object>();
- p.add(id);
- //
- return this.getDatabaseConnection().executePreparedStatement(" DELETE FROM \"" + this.getTable() + "\""
- + " WHERE (\"" + this.getTable() + "\".\"" + this.getPrimaryKey() + "\" = ?)",
- p);
- }
-
- public MSqlStatementResults deleteRecords(LinkedHashSet idSet) throws MDatabaseConnectionFailureException, MSqlStatementException {
- if (null == idSet) {
- throw new IllegalArgumentException("Invalid 'idSet': null.");
- }
- //
- String whereClause = null;
- LinkedList<Object> p = null;
- if (0 == idSet.size()) {
- whereClause = "0";
- }
- else {
- StringJoiner s = new StringJoiner(", ");
- p = new LinkedList<Object>();
- for (Object id: idSet) {
- s.add("?");
- p.add(id);
- }
- whereClause = "\"" + this.getTable() + "\".\"" + this.getPrimaryKey() + "\" IN (" + s.toString() + ")";
- }
- //
- return this.getDatabaseConnection().executePreparedStatement(" DELETE FROM \"" + this.getTable() + "\""
- + " WHERE (" + whereClause + ")",
- p);
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
- * See LICENSE for details.
- */
-
-package com.marcozanon.macaco.database;
-
-@SuppressWarnings("serial")
-public class MSqlTransactionException extends MDatabaseException {
-
- /* */
-
- public MSqlTransactionException() {
- super();
- }
-
- public MSqlTransactionException(String message) {
- super(message);
- }
-
- public MSqlTransactionException(Throwable error) {
- super(error);
- }
-
- public MSqlTransactionException(String message, Throwable error) {
- super(message, error);
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
- * See LICENSE for details.
- */
-
-package com.marcozanon.macaco.json;
-
-@SuppressWarnings("serial")
-public class MInvalidJsonValueException extends MJsonException {
-
- /* */
-
- public MInvalidJsonValueException() {
- super();
- }
-
- public MInvalidJsonValueException(String message) {
- super(message);
- }
-
- public MInvalidJsonValueException(Throwable error) {
- super(error);
- }
-
- public MInvalidJsonValueException(String message, Throwable error) {
- super(message, error);
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
- * See LICENSE for details.
- */
-
-package com.marcozanon.macaco.json;
-
-import com.marcozanon.macaco.text.MText;
-import java.util.LinkedList;
-
-public class MJsonArray extends MJsonValue {
-
- protected static enum ParsingMode {
- START,
- POST_VALUE,
- POST_VALUE_SEPARATOR,
- ERROR
- };
-
- protected LinkedList<MJsonValue> values = new LinkedList<MJsonValue>();
-
- /* */
-
- public MJsonArray() throws MInvalidJsonValueException {
- this("[]");
- }
-
- public MJsonArray(String x) throws MInvalidJsonValueException {
- super();
- //
- this.parseString(x);
- }
-
- @Override
- public MJsonArray clone() {
- MJsonArray tmpMJsonArray = null;
- try {
- tmpMJsonArray = new MJsonArray(this.getJsonValue());
- }
- catch (MInvalidJsonValueException exception) { // cannot happen
- }
- return tmpMJsonArray;
- }
-
- /* Value handlers. */
-
- public void addValue(MJsonValue x) {
- if (null == x) {
- throw new IllegalArgumentException("Invalid 'x': null.");
- }
- //
- this.getValues().add(x);
- }
-
- public void setValue(int index, MJsonValue x) {
- if ((0 > index) || ((this.getValueCount() - 1) < index)) {
- throw new IllegalArgumentException(String.format("Invalid 'index': %s: out of range.", index));
- }
- if (null == x) {
- throw new IllegalArgumentException("Invalid 'x': null.");
- }
- //
- this.getValues().set(index, x);
- }
-
- protected LinkedList<MJsonValue> getValues() {
- return this.values;
- }
-
- public MJsonValue getValue(int index) {
- if ((0 > index) || ((this.getValueCount() - 1) < index)) {
- throw new IllegalArgumentException(String.format("Invalid 'index': %s: out of range.", index));
- }
- //
- return this.getValues().get(index).clone();
- }
-
- public int getValueCount() {
- return this.getValues().size();
- }
-
- public void removeValue(int index) {
- if ((0 > index) || ((this.getValueCount() - 1) < index)) {
- throw new IllegalArgumentException(String.format("Invalid 'index': %s: out of range.", index));
- }
- //
- this.getValues().remove(index);
- }
-
- public void clearValues() {
- this.getValues().clear();
- }
-
- /* Parsers. */
-
- protected static int getTokenLength(String x) {
- if (null == x) {
- throw new IllegalArgumentException("Invalid 'x': null.");
- }
- //
- if ((2 > x.length()) || ('[' != x.charAt(0))) {
- return 0;
- }
- int position = x.indexOf("]", 1);
- while (-1 < position) {
- try {
- MJsonArray testValue = new MJsonArray();
- testValue.parseString(x.substring(0, position + 1));
- return position + 1;
- }
- catch (MInvalidJsonValueException exception) {
- position = x.indexOf("]", position + 1);
- }
- }
- return 0;
- }
-
- public void parseString(String x) throws MInvalidJsonValueException {
- if (MText.isBlank(x)) {
- throw new IllegalArgumentException("Invalid 'x': null or empty.");
- }
- //
- if ((2 > x.length()) || ('[' != x.charAt(0)) || (']' != x.charAt(x.length() - 1))) {
- throw new MInvalidJsonValueException(String.format("Invalid 'x': %s: must begin and end with square brackets.", x));
- }
- String y = x.substring(1, x.length() - 1);
- int parsingPosition = 0;
- MJsonArray.ParsingMode parsingMode = MJsonArray.ParsingMode.START;
- while ((y.length() > parsingPosition) && (MJsonArray.ParsingMode.ERROR != parsingMode)) {
- int c = y.codePointAt(parsingPosition);
- switch (parsingMode) {
- case START:
- case POST_VALUE_SEPARATOR: // a value is expected
- if (MJsonValue.isWhitespace(c)) {
- parsingPosition++;
- }
- else if ((MJsonValue.isValueSeparator(c)) || (MJsonValue.isNameSeparator(c)) || (MJsonValue.isClosingStructuralCharacter(c))) {
- parsingMode = MJsonArray.ParsingMode.ERROR;
- }
- else {
- int offset = 0;
- if (0 < (offset = MJsonNull.getTokenLength(y.substring(parsingPosition)))) {
- this.addValue(new MJsonNull(y.substring(parsingPosition, parsingPosition + offset)));
- }
- else if (0 < (offset = MJsonBoolean.getTokenLength(y.substring(parsingPosition)))) {
- this.addValue(new MJsonBoolean(y.substring(parsingPosition, parsingPosition + offset)));
- }
- else if (0 < (offset = MJsonNumber.getTokenLength(y.substring(parsingPosition)))) {
- this.addValue(new MJsonNumber(y.substring(parsingPosition, parsingPosition + offset)));
- }
- else if (0 < (offset = MJsonString.getTokenLength(y.substring(parsingPosition)))) {
- this.addValue(new MJsonString(y.substring(parsingPosition, parsingPosition + offset)));
- }
- else if (0 < (offset = MJsonArray.getTokenLength(y.substring(parsingPosition)))) {
- this.addValue(new MJsonArray(y.substring(parsingPosition, parsingPosition + offset)));
- }
- else if (0 < (offset = MJsonObject.getTokenLength(y.substring(parsingPosition)))) {
- this.addValue(new MJsonObject(y.substring(parsingPosition, parsingPosition + offset)));
- }
- if (0 < offset) {
- parsingPosition += offset;
- parsingMode = MJsonArray.ParsingMode.POST_VALUE;
- }
- else {
- parsingMode = MJsonArray.ParsingMode.ERROR;
- }
- }
- break;
- case POST_VALUE:
- if (MJsonValue.isWhitespace(c)) {
- parsingPosition++;
- }
- else if (MJsonValue.isValueSeparator(c)) {
- parsingPosition++;
- parsingMode = MJsonArray.ParsingMode.POST_VALUE_SEPARATOR;
- }
- else {
- parsingMode = MJsonArray.ParsingMode.ERROR;
- }
- break;
- case ERROR: // malformed string
- break;
- }
- }
- if (MJsonArray.ParsingMode.POST_VALUE_SEPARATOR == parsingMode) {
- parsingMode = MJsonArray.ParsingMode.ERROR;
- }
- if (MJsonArray.ParsingMode.ERROR == parsingMode) {
- throw new MInvalidJsonValueException(String.format("Invalid 'x': %s: not a Json array.", x));
- }
- }
-
- /* Formatter. */
-
- @Override
- public String getJsonValue() {
- StringBuilder s = new StringBuilder("");
- s.append("[");
- for (int i = 0; this.getValueCount() > i; i++) {
- if (0 < i) {
- s.append(", ");
- }
- s.append(this.getValues().get(i).getJsonValue());
- }
- s.append("]");
- return s.toString();
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
- * See LICENSE for details.
- */
-
-package com.marcozanon.macaco.json;
-
-import com.marcozanon.macaco.text.MText;
-
-public class MJsonBoolean extends MJsonValue {
-
- protected Boolean value = null;
-
- /* */
-
- public MJsonBoolean() throws MInvalidJsonValueException {
- this("false");
- }
-
- public MJsonBoolean(String x) throws MInvalidJsonValueException {
- super();
- //
- this.parseString(x);
- }
-
- @Override
- public MJsonBoolean clone() {
- MJsonBoolean tmpMJsonBoolean = null;
- try {
- tmpMJsonBoolean = new MJsonBoolean(this.getJsonValue());
- }
- catch (MInvalidJsonValueException exception) { // cannot happen
- }
- return tmpMJsonBoolean;
- }
-
- /* Value handlers. */
-
- public void setValue(Boolean x) {
- if (null == x) {
- throw new IllegalArgumentException("Invalid 'x': null.");
- }
- //
- this.value = x;
- }
-
- public Boolean getValue() {
- return this.value;
- }
-
- /* Parsers. */
-
- protected static int getTokenLength(String x) {
- if (null == x) {
- throw new IllegalArgumentException("Invalid 'x': null.");
- }
- //
- if (x.startsWith("true")) {
- return "true".length();
- }
- else if (x.startsWith("false")) {
- return "false".length();
- }
- return 0;
- }
-
- public void parseString(String x) throws MInvalidJsonValueException {
- if (MText.isBlank(x)) {
- throw new IllegalArgumentException("Invalid 'x': null or empty.");
- }
- //
- if ((!"true".equals(x)) && (!"false".equals(x))) {
- throw new MInvalidJsonValueException(String.format("Invalid 'x': %s: not a Json boolean.", x));
- }
- this.setValue(Boolean.valueOf(x));
- }
-
- /* Formatter. */
-
- @Override
- public String getJsonValue() {
- return this.getValue().toString();
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
- * See LICENSE for details.
- */
-
-package com.marcozanon.macaco.json;
-
-import com.marcozanon.macaco.MException;
-
-public abstract class MJsonException extends MException {
-
- /* */
-
- public MJsonException() {
- super();
- }
-
- public MJsonException(String message) {
- super(message);
- }
-
- public MJsonException(Throwable error) {
- super(error);
- }
-
- public MJsonException(String message, Throwable error) {
- super(message, error);
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
- * See LICENSE for details.
- */
-
-package com.marcozanon.macaco.json;
-
-import com.marcozanon.macaco.text.MText;
-
-public class MJsonNull extends MJsonValue {
-
- /* */
-
- public MJsonNull() throws MInvalidJsonValueException {
- this("null");
- }
-
- public MJsonNull(String x) throws MInvalidJsonValueException {
- super();
- //
- this.parseString(x);
- }
-
- @Override
- public MJsonNull clone() {
- MJsonNull tmpMJsonNull = null;
- try {
- tmpMJsonNull = new MJsonNull(this.getJsonValue());
- }
- catch (MInvalidJsonValueException exception) { // cannot happen
- }
- return tmpMJsonNull;
- }
-
- /* Parsers. */
-
- protected static int getTokenLength(String x) {
- if (null == x) {
- throw new IllegalArgumentException("Invalid 'x': null.");
- }
- //
- if (x.startsWith("null")) {
- return "null".length();
- }
- return 0;
- }
-
- public void parseString(String x) throws MInvalidJsonValueException {
- if (MText.isBlank(x)) {
- throw new IllegalArgumentException("Invalid 'x': null or empty.");
- }
- //
- if (!"null".equals(x)) {
- throw new MInvalidJsonValueException(String.format("Invalid 'x': %s: not a Json null.", x));
- }
- }
-
- /* Formatter. */
-
- @Override
- public String getJsonValue() {
- return "null";
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
- * See LICENSE for details.
- */
-
-package com.marcozanon.macaco.json;
-
-import com.marcozanon.macaco.text.MText;
-import java.math.BigDecimal;
-
-public class MJsonNumber extends MJsonValue {
-
- protected BigDecimal value = null;
-
- /* */
-
- public MJsonNumber() throws MInvalidJsonValueException {
- this("0");
- }
-
- public MJsonNumber(String x) throws MInvalidJsonValueException {
- super();
- //
- this.parseString(x);
- }
-
- @Override
- public MJsonNumber clone() {
- MJsonNumber tmpMJsonNumber = null;
- try {
- tmpMJsonNumber = new MJsonNumber(this.getJsonValue());
- }
- catch (MInvalidJsonValueException exception) { // cannot happen
- }
- return tmpMJsonNumber;
- }
-
- /* Value handlers. */
-
- public void setValue(Number x) {
- if (null == x) {
- throw new IllegalArgumentException("Invalid 'x': null.");
- }
- //
- this.value = new BigDecimal(x.toString());
- }
-
- public BigDecimal getValue() {
- return this.value;
- }
-
- /* Parsers. */
-
- protected static int getTokenLength(String x) {
- if (null == x) {
- throw new IllegalArgumentException("Invalid 'x': null.");
- }
- //
- if (' ' == x.charAt(0)) {
- return 0;
- }
- int position = 0;
- int validPosition = 0;
- BigDecimal y = null;
- while (x.length() > position) {
- if (' ' == x.charAt(position)) {
- break;
- }
- else {
- try {
- y = new BigDecimal(x.substring(0, position + 1));
- position++;
- validPosition = position;
- }
- catch (NumberFormatException exception) {
- position++;
- }
- }
- }
- return validPosition;
- }
-
- public void parseString(String x) throws MInvalidJsonValueException {
- if (MText.isBlank(x)) {
- throw new IllegalArgumentException("Invalid 'x': null or empty.");
- }
- //
- BigDecimal y = null;
- try {
- y = new BigDecimal(x);
- }
- catch (NumberFormatException exception) {
- throw new MInvalidJsonValueException(String.format("Invalid 'x': %s: not a Json number.", x)); // no need to propagate exception
- }
- this.setValue(y);
- }
-
- /* Formatter. */
-
- @Override
- public String getJsonValue() {
- return this.getValue().toString();
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
- * See LICENSE for details.
- */
-
-package com.marcozanon.macaco.json;
-
-import com.marcozanon.macaco.text.MText;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-
-public class MJsonObject extends MJsonValue {
-
- protected static enum ParsingMode {
- START,
- POST_NAME,
- POST_NAME_SEPARATOR,
- POST_VALUE,
- POST_VALUE_SEPARATOR,
- ERROR
- };
-
- protected LinkedHashMap<String, MJsonValue> values = new LinkedHashMap<String, MJsonValue>();
-
- /* */
-
- public MJsonObject() throws MInvalidJsonValueException {
- this("{}");
- }
-
- public MJsonObject(String x) throws MInvalidJsonValueException {
- super();
- //
- this.parseString(x);
- }
-
- @Override
- public MJsonObject clone() {
- MJsonObject tmpMJsonObject = null;
- try {
- tmpMJsonObject = new MJsonObject(this.getJsonValue());
- }
- catch (MInvalidJsonValueException exception) { // cannot happen
- }
- return tmpMJsonObject;
- }
-
- /* Value handlers. */
-
- public void setValue(String key, MJsonValue x) {
- if (MText.isBlank(key)) {
- throw new IllegalArgumentException("Invalid 'key': null or empty.");
- }
- if (null == x) {
- throw new IllegalArgumentException("Invalid 'x': null.");
- }
- //
- this.getValues().put(key, x);
- }
-
- protected LinkedHashMap<String, MJsonValue> getValues() {
- return this.values;
- }
-
- public MJsonValue getValue(String key) {
- if (MText.isBlank(key)) {
- throw new IllegalArgumentException("Invalid 'key': null or empty.");
- }
- //
- if (!this.containsKey(key)) {
- throw new IllegalArgumentException(String.format("Invalid 'key': %s: not available.", key));
- }
- return this.getValues().get(key).clone();
- }
-
- public int getValueCount() {
- return this.getValues().size();
- }
-
- public void removeValue(String key) {
- if (MText.isBlank(key)) {
- throw new IllegalArgumentException("Invalid 'key': null or empty.");
- }
- //
- if (!this.containsKey(key)) {
- throw new IllegalArgumentException(String.format("Invalid 'key': %s: not available.", key));
- }
- this.getValues().remove(key);
- }
-
- public void clearValues() {
- this.getValues().clear();
- }
-
- public boolean containsKey(String key) {
- if (MText.isBlank(key)) {
- throw new IllegalArgumentException("Invalid 'key': null or empty.");
- }
- //
- return this.getValues().containsKey(key);
- }
-
- public LinkedHashSet<String> getKeys() {
- LinkedHashSet<String> keys = new LinkedHashSet<String>();
- for (String key: this.getValues().keySet()) {
- keys.add(key);
- }
- return keys;
- }
-
- /* Parsers. */
-
- protected static int getTokenLength(String x) {
- if (null == x) {
- throw new IllegalArgumentException("Invalid 'x': null.");
- }
- //
- if ((2 > x.length()) || ('{' != x.charAt(0))) {
- return 0;
- }
- int position = x.indexOf("}", 1);
- while (-1 < position) {
- try {
- MJsonObject testValue = new MJsonObject();
- testValue.parseString(x.substring(0, position + 1));
- return position + 1;
- }
- catch (MInvalidJsonValueException exception) {
- position = x.indexOf("}", position + 1);
- }
- }
- return 0;
- }
-
- public void parseString(String x) throws MInvalidJsonValueException {
- if (MText.isBlank(x)) {
- throw new IllegalArgumentException("Invalid 'x': null or empty.");
- }
- //
- if ((2 > x.length()) || ('{' != x.charAt(0)) || ('}' != x.charAt(x.length() - 1))) {
- throw new MInvalidJsonValueException(String.format("Invalid 'x': %s: must begin and end with curly brackets.", x));
- }
- String y = x.substring(1, x.length() - 1);
- int parsingPosition = 0;
- MJsonObject.ParsingMode parsingMode = MJsonObject.ParsingMode.START;
- String lastKey = "";
- while ((y.length() > parsingPosition) && (MJsonObject.ParsingMode.ERROR != parsingMode)) {
- int c = y.codePointAt(parsingPosition);
- switch (parsingMode) {
- case START:
- case POST_VALUE_SEPARATOR: // a name is expected
- if (MJsonValue.isWhitespace(c)) {
- parsingPosition++;
- }
- else if ((MJsonValue.isValueSeparator(c)) || (MJsonValue.isNameSeparator(c)) || (MJsonValue.isOpeningStructuralCharacter(c)) || (MJsonValue.isClosingStructuralCharacter(c))) {
- parsingMode = MJsonObject.ParsingMode.ERROR;
- }
- else {
- int offset = MJsonString.getTokenLength(y.substring(parsingPosition));
- if (0 < offset) {
- lastKey = (new MJsonString(y.substring(parsingPosition, parsingPosition + offset))).getValue();
- parsingPosition += offset;
- parsingMode = MJsonObject.ParsingMode.POST_NAME;
- }
- else {
- parsingMode = MJsonObject.ParsingMode.ERROR;
- }
- }
- break;
- case POST_NAME:
- if (MJsonValue.isWhitespace(c)) {
- parsingPosition++;
- }
- else if (MJsonValue.isNameSeparator(c)) {
- parsingPosition++;
- parsingMode = MJsonObject.ParsingMode.POST_NAME_SEPARATOR;
- }
- else {
- parsingMode = MJsonObject.ParsingMode.ERROR;
- }
- break;
- case POST_NAME_SEPARATOR: // a value is expected
- if (MJsonValue.isWhitespace(c)) {
- parsingPosition++;
- }
- else if ((MJsonValue.isValueSeparator(c)) || (MJsonValue.isNameSeparator(c)) || (MJsonValue.isClosingStructuralCharacter(c))) {
- parsingMode = MJsonObject.ParsingMode.ERROR;
- }
- else {
- int offset = 0;
- if (0 < (offset = MJsonNull.getTokenLength(y.substring(parsingPosition)))) {
- this.setValue(lastKey, new MJsonNull(y.substring(parsingPosition, parsingPosition + offset)));
- }
- else if (0 < (offset = MJsonBoolean.getTokenLength(y.substring(parsingPosition)))) {
- this.setValue(lastKey, new MJsonBoolean(y.substring(parsingPosition, parsingPosition + offset)));
- }
- else if (0 < (offset = MJsonNumber.getTokenLength(y.substring(parsingPosition)))) {
- this.setValue(lastKey, new MJsonNumber(y.substring(parsingPosition, parsingPosition + offset)));
- }
- else if (0 < (offset = MJsonString.getTokenLength(y.substring(parsingPosition)))) {
- this.setValue(lastKey, new MJsonString(y.substring(parsingPosition, parsingPosition + offset)));
- }
- else if (0 < (offset = MJsonArray.getTokenLength(y.substring(parsingPosition)))) {
- this.setValue(lastKey, new MJsonArray(y.substring(parsingPosition, parsingPosition + offset)));
- }
- else if (0 < (offset = MJsonObject.getTokenLength(y.substring(parsingPosition)))) {
- this.setValue(lastKey, new MJsonObject(y.substring(parsingPosition, parsingPosition + offset)));
- }
- if (0 < offset) {
- lastKey = "";
- parsingPosition += offset;
- parsingMode = MJsonObject.ParsingMode.POST_VALUE;
- }
- else {
- parsingMode = MJsonObject.ParsingMode.ERROR;
- }
- }
- break;
- case POST_VALUE:
- if (MJsonValue.isWhitespace(c)) {
- parsingPosition++;
- }
- else if (MJsonValue.isValueSeparator(c)) {
- parsingPosition++;
- parsingMode = MJsonObject.ParsingMode.POST_VALUE_SEPARATOR;
- }
- else {
- parsingMode = MJsonObject.ParsingMode.ERROR;
- }
- break;
- case ERROR: // malformed string
- break;
- }
- }
- if ((MJsonObject.ParsingMode.POST_NAME == parsingMode) || (MJsonObject.ParsingMode.POST_NAME_SEPARATOR == parsingMode) || (MJsonObject.ParsingMode.POST_VALUE_SEPARATOR == parsingMode)) {
- parsingMode = MJsonObject.ParsingMode.ERROR;
- }
- if (MJsonObject.ParsingMode.ERROR == parsingMode) {
- throw new MInvalidJsonValueException(String.format("Invalid 'x': %s: not a Json object.", x));
- }
- }
-
- /* Formatter. */
-
- @Override
- public String getJsonValue() {
- StringBuilder s = new StringBuilder("");
- s.append("{");
- int k = 0;
- for (String key: this.getValues().keySet()) {
- if (0 < k) {
- s.append(", ");
- }
- s.append("\"" + MJsonString.getEscapedString(key, false) + "\"");
- s.append(": ");
- s.append(this.getValues().get(key).getJsonValue());
- k++;
- }
- s.append("}");
- return s.toString();
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
- * See LICENSE for details.
- */
-
-package com.marcozanon.macaco.json;
-
-import com.marcozanon.macaco.text.MText;
-
-public class MJsonString extends MJsonValue {
-
- protected boolean extendedEscapeMode = false;
-
- protected String value = null;
-
- /* */
-
- public MJsonString() throws MInvalidJsonValueException {
- this("\"\"");
- }
-
- public MJsonString(String x) throws MInvalidJsonValueException {
- this(x, false);
- }
-
- public MJsonString(String x, boolean extendedEscapeMode) throws MInvalidJsonValueException {
- super();
- //
- this.setExtendedEscapeMode(extendedEscapeMode);
- this.parseString(x);
- }
-
- @Override
- public MJsonString clone() {
- MJsonString tmpMJsonString = null;
- try {
- tmpMJsonString = new MJsonString(this.getJsonValue());
- }
- catch (MInvalidJsonValueException exception) { // cannot happen
- }
- return tmpMJsonString;
- }
-
- /* Extended escape mode. */
-
- public void setExtendedEscapeMode(boolean extendedEscapeMode) {
- this.extendedEscapeMode = extendedEscapeMode;
- }
-
- public boolean getExtendedEscapeMode() {
- return this.extendedEscapeMode;
- }
-
- /* Value handlers. */
-
- public void setValue(String x) {
- if (null == x) {
- throw new IllegalArgumentException("Invalid 'x': null.");
- }
- //
- this.value = x;
- }
-
- public String getValue() {
- return this.value;
- }
-
- /* Helpers. */
-
- protected static String getEscapedString(String x, boolean extendedEscapeMode) {
- if (null == x) {
- throw new IllegalArgumentException("Invalid 'x': null.");
- }
- //
- StringBuilder y = new StringBuilder();
- for (int i = 0; x.length() > i; i++) {
- int c = x.codePointAt(i);
- if (extendedEscapeMode) {
- if (0x00001F >= c) { // control Ascii character (0x000000-0x00001F)
- y.append("\\u" + String.format("%4H", c).replace(" ", "0"));
- }
- else if (0x00007F >= c) { // non-control Ascii character (0x000020-0x00007F)
- if ('"' == c) {
- y.append("\\\"");
- }
- else if ('\\' == c) {
- y.append("\\\\");
- }
- else if ('/' == c) {
- y.append("\\/");
- }
- else {
- y.appendCodePoint(c);
- }
- }
- else if (0x00FFFF >= c) { // non-Ascii Bmp character (0x000080-0x00FFFF)
- y.append("\\u" + String.format("%4H", c).replace(" ", "0"));
- }
- else { // non-Bmp character (0x010000-0x10FFFF)
- int cc = c - 0x010000;
- int ch = 0xD800 + (cc >> 10);
- int cl = 0xDC00 + (cc & 0x03FF);
- y.append("\\u" + String.format("%4H", ch).replace(" ", "0") + "\\u" + String.format("%4H", cl).replace(" ", "0"));
- i++;
- }
- }
- else {
- if ('"' == c) {
- y.append("\\\"");
- }
- else if ('\\' == c) {
- y.append("\\\\");
- }
- else if ('/' == c) {
- y.append("\\/");
- }
- else if ('\b' == c) {
- y.append("\\b");
- }
- else if ('\f' == c) {
- y.append("\\f");
- }
- else if ('\n' == c) {
- y.append("\\n");
- }
- else if ('\r' == c) {
- y.append("\\r");
- }
- else if ('\t' == c) {
- y.append("\\t");
- }
- else if (0x00001F >= c) { // non-special control Ascii character (0x000000-0x00001F)
- y.append("\\u" + String.format("%4H", c).replace(" ", "0"));
- }
- else {
- y.appendCodePoint(c);
- if (0x010000 <= c) {
- i++;
- }
- }
- }
- }
- return y.toString();
- }
-
- protected static String getUnescapedString(String x) throws MInvalidJsonValueException {
- if (null == x) {
- throw new IllegalArgumentException("Invalid 'x': null.");
- }
- //
- StringBuilder y = new StringBuilder();
- for (int i = 0; x.length() > i; i++) {
- if (x.indexOf("\\\"", i) == i) {
- y.append("\"");
- i++;
- }
- else if (x.indexOf("\\\\", i) == i) {
- y.append("\\");
- i++;
- }
- else if (x.indexOf("\\/", i) == i) {
- y.append("/");
- i++;
- }
- else if (x.indexOf("\\b", i) == i) {
- y.append("\b");
- i++;
- }
- else if (x.indexOf("\\f", i) == i) {
- y.append("\f");
- i++;
- }
- else if (x.indexOf("\\n", i) == i) {
- y.append("\n");
- i++;
- }
- else if (x.indexOf("\\r", i) == i) {
- y.append("\r");
- i++;
- }
- else if (x.indexOf("\\t", i) == i) {
- y.append("\t");
- i++;
- }
- else if (x.indexOf("\\u", i) == i) {
- if ((i + 2 + 4) > x.length()) {
- throw new MInvalidJsonValueException(String.format("Invalid 'x': %s: not a Json string.", x));
- }
- else {
- int ch = -1;
- try {
- ch = Integer.parseInt(x.substring(i + 2, i + 2 + 4), 16);
- i += 5;
- if ((0xD800 <= ch) && (0xDBFF >= ch)) {
- i++;
- if (x.indexOf("\\u", i) != i) {
- throw new MInvalidJsonValueException(String.format("Invalid 'x': %s: not a Json string.", x));
- }
- else if ((i + 2 + 4) > x.length()) {
- throw new MInvalidJsonValueException(String.format("Invalid 'x': %s: not a Json string.", x));
- }
- else {
- int cl = -1;
- try {
- cl = Integer.parseInt(x.substring(i + 2, i + 2 + 4), 16);
- i += 5;
- if ((0xDC00 >= cl) || (0xDFFF <= cl)) {
- throw new MInvalidJsonValueException(String.format("Invalid 'x': %s: not a Json string.", x));
- }
- int c = ((ch - 0xD800) << 10) + (cl - 0xDC00) + 0x010000;
- y.appendCodePoint(c);
- }
- catch (NumberFormatException exception) {
- throw new MInvalidJsonValueException(String.format("Invalid 'x': %s: not a Json string.", x)); // no need to propagate exception
- }
- }
- }
- else {
- y.appendCodePoint(ch);
- }
- }
- catch (NumberFormatException exception) {
- throw new MInvalidJsonValueException(String.format("Invalid 'x': %s: not a Json string.", x)); // no need to propagate exception
- }
- }
- }
- else {
- int c = x.codePointAt(i);
- if (0x010000 <= c) {
- i++;
- }
- y.appendCodePoint(c);
- }
- }
- return y.toString();
- }
-
- /* Parsers. */
-
- protected static int getTokenLength(String x) {
- if (null == x) {
- throw new IllegalArgumentException("Invalid 'x': null.");
- }
- //
- if ((2 > x.length()) || ('"' != x.charAt(0))) {
- return 0;
- }
- if ('"' == x.charAt(1)) {
- return 1 + 1;
- }
- String y = x.substring(1);
- int quotationMarkPosition = 0;
- while (-1 < (quotationMarkPosition = y.indexOf("\"", quotationMarkPosition + 1))) {
- int c = 0;
- StringBuilder s = new StringBuilder("");
- while ((c <= quotationMarkPosition) && (y.substring(quotationMarkPosition - c, quotationMarkPosition).equals(s.toString()))) {
- c++;
- s.append("\\");
- }
- c--;
- if (0 == (c % 2)) {
- break;
- }
- }
- if (-1 == quotationMarkPosition) {
- return 0;
- }
- return (quotationMarkPosition + 1) + 1;
- }
-
- public void parseString(String x) throws MInvalidJsonValueException {
- if (MText.isBlank(x)) {
- throw new IllegalArgumentException("Invalid 'x': null or empty.");
- }
- //
- if ((2 > x.length()) || ('"' != x.charAt(0)) || ('"' != x.charAt(x.length() - 1))) {
- throw new MInvalidJsonValueException(String.format("Invalid 'x': %s: not a Json string.", x));
- }
- String y = x.substring(1, x.length() - 1);
- if (-1 < y.replaceAll("\\\\\"", "__").indexOf("\"")) {
- throw new MInvalidJsonValueException(String.format("Invalid 'x': %s: not a Json string.", x));
- }
- this.setValue(MJsonString.getUnescapedString(y));
- }
-
- /* Formatter. */
-
- @Override
- public String getJsonValue() {
- return "\"" + MJsonString.getEscapedString(this.getValue(), this.getExtendedEscapeMode()) + "\"";
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
- * See LICENSE for details.
- */
-
-package com.marcozanon.macaco.json;
-
-import com.marcozanon.macaco.MObject;
-
-public abstract class MJsonValue extends MObject {
-
- /* */
-
- @Override
- public abstract MJsonValue clone();
-
- /* Helpers. */
-
- protected static boolean isWhitespace(int c) {
- return ((' ' == c) || ('\t' == c) || ('\n' == c) || ('\r' == c));
- }
-
- protected static boolean isValueSeparator(int c) {
- return (',' == c);
- }
-
- protected static boolean isNameSeparator(int c) {
- return (':' == c);
- }
-
- protected static boolean isOpeningStructuralCharacter(int c) {
- return (('[' == c) || ('{' == c));
- }
-
- protected static boolean isClosingStructuralCharacter(int c) {
- return ((']' == c) || ('}' == c));
- }
-
- /* Formatter. */
-
- public abstract String getJsonValue();
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
- * See LICENSE for details.
- */
-
-package com.marcozanon.macaco.logging;
-
-import com.marcozanon.macaco.database.MDatabaseConnection;
-import com.marcozanon.macaco.database.MDatabaseConnectionFailureException;
-import com.marcozanon.macaco.database.MDatabaseConnectionPool;
-import com.marcozanon.macaco.database.MSqlStatementException;
-import com.marcozanon.macaco.database.MSqlTable;
-import com.marcozanon.macaco.text.MText;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.LinkedHashMap;
-
-public class MLogDatabaseTable extends MLogTarget {
-
- protected MDatabaseConnectionPool databaseConnectionPool = null;
-
- protected String logDatabaseTable = null;
- protected String logDatabaseTablePrimaryKey = null;
- protected String logDatabaseField = null;
-
- protected MDatabaseConnection databaseConnection = null;
-
- /* */
-
- public MLogDatabaseTable(MDatabaseConnectionPool databaseConnectionPool, String logDatabaseTable, String logDatabaseTablePrimaryKey, String logDatabaseField) throws MLoggingException {
- super();
- //
- if (null == databaseConnectionPool) {
- throw new IllegalArgumentException("Invalid 'databaseConnectionPool': null.");
- }
- if (MText.isEmpty(logDatabaseTable)) {
- throw new IllegalArgumentException("Invalid 'logDatabaseTable': null or empty.");
- }
- if (MText.isEmpty(logDatabaseTablePrimaryKey)) {
- throw new IllegalArgumentException("Invalid 'logDatabaseTablePrimaryKey': null or empty.");
- }
- if (MText.isEmpty(logDatabaseField)) {
- throw new IllegalArgumentException("Invalid 'logDatabaseField': null or empty.");
- }
- //
- this.databaseConnectionPool = databaseConnectionPool;
- this.logDatabaseTable = logDatabaseTable;
- this.logDatabaseTablePrimaryKey = logDatabaseTablePrimaryKey;
- this.logDatabaseField = logDatabaseField;
- }
-
- @Override
- public void close() throws MLoggingException {
- }
-
- /* Database connection pool. */
-
- protected MDatabaseConnectionPool getDatabaseConnectionPool() {
- return this.databaseConnectionPool;
- }
-
- /* Database logging parameters. */
-
- protected String getLogDatabaseTable() {
- return this.logDatabaseTable;
- }
-
- protected String getLogDatabaseTablePrimaryKey() {
- return this.logDatabaseTablePrimaryKey;
- }
-
- protected String getLogDatabaseField() {
- return this.logDatabaseField;
- }
-
- /* Output. */
-
- @Override
- public void appendMessage(String message) throws MLoggingException {
- this.appendMessage(message, 0);
- }
-
- @Override
- public void appendMessage(String message, int indentation) throws MLoggingException {
- if (null == message) {
- throw new IllegalArgumentException("Invalid 'message': null.");
- }
- if (0 > indentation) {
- throw new IllegalArgumentException(String.format("Invalid 'indentation': %s: cannot be negative.", indentation));
- }
- //
- try {
- String timestamp = (new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")).format(new Date());
- //
- StringBuilder separator = new StringBuilder(" ");
- for (int i = 0; i < indentation; i++) {
- separator.append(" ");
- }
- //
- MDatabaseConnection databaseConnection = this.getDatabaseConnectionPool().popDatabaseConnection();
- //
- LinkedHashMap<String, Object> p = new LinkedHashMap<String, Object>();
- p.put(this.getLogDatabaseField(), timestamp + separator + message);
- (new MSqlTable(databaseConnection, this.getLogDatabaseTable(), this.getLogDatabaseTablePrimaryKey())).setRecord(p, null, false);
- //
- this.getDatabaseConnectionPool().pushDatabaseConnection(databaseConnection);
- }
- catch (MDatabaseConnectionFailureException exception) {
- throw new MLoggingException("Could not write to database table.", exception);
- }
- catch (MSqlStatementException exception) {
- throw new MLoggingException("Could not write to database table.", exception);
- }
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
- * See LICENSE for details.
- */
-
-package com.marcozanon.macaco.logging;
-
-import com.marcozanon.macaco.MObject;
-import java.util.LinkedList;
-
-public class MLogFilter extends MObject {
-
- public static enum Threshold {
- STANDARD,
- DEBUG
- };
-
- protected MLogFilter.Threshold threshold = null;
-
- protected LinkedList<MLogTarget> logTargets = new LinkedList<MLogTarget>();
-
- protected boolean pausedState = false;
- protected LinkedList<MLogMessage> logMessageQueue = new LinkedList<MLogMessage>();
-
- /* */
-
- public MLogFilter(MLogFilter.Threshold threshold) {
- super();
- //
- this.setThreshold(threshold);
- }
-
- public void close() throws MLoggingException {
- for (MLogTarget t: this.getLogTargets()) {
- t.close();
- }
- }
-
- @Override
- protected void finalize() {
- try {
- this.close();
- }
- catch (Exception exception) {
- }
- }
-
- /* Threshold. */
-
- public void setThreshold(MLogFilter.Threshold threshold) {
- if (null == threshold) {
- throw new IllegalArgumentException("Invalid 'threshold': null.");
- }
- //
- this.threshold = threshold;
- }
-
- public MLogFilter.Threshold getThreshold() {
- return this.threshold;
- }
-
- /* Log targets. */
-
- public void addLogTarget(MLogTarget logTarget) {
- if (null == logTarget) {
- throw new IllegalArgumentException("Invalid 'logTarget': null.");
- }
- //
- synchronized (this.logTargets) {
- this.logTargets.add(logTarget);
- }
- }
-
- protected LinkedList<MLogTarget> getLogTargets() {
- return this.logTargets;
- }
-
- /* Output. */
-
- public void setPausedState(boolean pausedState) throws MLoggingException {
- this.pausedState = pausedState;
- //
- if (!this.getPausedState()) {
- this.flushMessages();
- }
- }
-
- public boolean getPausedState() {
- return this.pausedState;
- }
-
- public void appendMessage(MLogFilter.Threshold level, String message) throws MLoggingException {
- this.appendMessage(level, message, 0);
- }
-
- public void appendMessage(MLogFilter.Threshold level, String message, int indentation) throws MLoggingException {
- if (null == level) {
- throw new IllegalArgumentException("Invalid 'level': null.");
- }
- //
- if (level.ordinal() > this.getThreshold().ordinal()) {
- return;
- }
- //
- this.logMessageQueue.add(new MLogMessage(message, indentation));
- //
- if (!this.getPausedState()) {
- this.flushMessages();
- }
- }
-
- protected void flushMessages() throws MLoggingException {
- while (0 < this.logMessageQueue.size()) {
- MLogMessage logMessage = this.logMessageQueue.remove();
- String message = logMessage.getMessage();
- int indentation = logMessage.getIndentation();
- //
- for (MLogTarget logTarget: this.getLogTargets()) {
- logTarget.appendMessage(message, indentation);
- }
- }
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
- * See LICENSE for details.
- */
-
-package com.marcozanon.macaco.logging;
-
-public interface MLogListener {
-
- /* Logging. */
-
- public void onMessageLogging(String message);
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
- * See LICENSE for details.
- */
-
-package com.marcozanon.macaco.logging;
-
-import com.marcozanon.macaco.MObject;
-
-public class MLogMessage extends MObject {
-
- protected String message = null;
- protected int indentation = 0;
-
- /* */
-
- public MLogMessage(String message, int indentation) {
- if (null == message) {
- throw new IllegalArgumentException("Invalid 'message': null.");
- }
- if (0 > indentation) {
- throw new IllegalArgumentException(String.format("Invalid 'indentation': %s: cannot be negative.", indentation));
- }
- //
- this.message = message;
- this.indentation = indentation;
- }
-
- /* Values. */
-
- public String getMessage() {
- return this.message;
- }
-
- public int getIndentation() {
- return this.indentation;
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
- * See LICENSE for details.
- */
-
-package com.marcozanon.macaco.logging;
-
-import com.marcozanon.macaco.MConstants;
-import com.marcozanon.macaco.text.MText;
-import java.io.BufferedWriter;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.io.UnsupportedEncodingException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-
-public class MLogPlainTextFile extends MLogTarget {
-
- protected String file = null;
-
- protected BufferedWriter buffer = null;
-
- /* */
-
- public MLogPlainTextFile(String file) throws MLoggingException {
- super();
- //
- if (MText.isBlank(file)) {
- throw new IllegalArgumentException("Invalid 'file': null or empty.");
- }
- //
- this.file = file;
- try {
- this.buffer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(this.getFile(), true), MConstants.TEXT_ENCODING));
- }
- catch (FileNotFoundException exception) {
- throw new MLoggingException("Could not open file.", exception);
- }
- catch (UnsupportedEncodingException exception) { // cannot happen
- }
- }
-
- @Override
- public void close() throws MLoggingException {
- try {
- this.getBuffer().close();
- }
- catch (IOException exception) {
- throw new MLoggingException("Could not close file.", exception);
- }
- }
-
- /* File. */
-
- protected String getFile() {
- return this.file;
- }
-
- /* Buffer. */
-
- protected BufferedWriter getBuffer() {
- return this.buffer;
- }
-
- /* Output. */
-
- @Override
- public void appendMessage(String message) throws MLoggingException {
- this.appendMessage(message, 0);
- }
-
- @Override
- public void appendMessage(String message, int indentation) throws MLoggingException {
- if (null == message) {
- throw new IllegalArgumentException("Invalid 'message': null.");
- }
- if (0 > indentation) {
- throw new IllegalArgumentException(String.format("Invalid 'indentation': %s: cannot be negative.", indentation));
- }
- //
- try {
- String timestamp = (new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")).format(new Date());
- //
- StringBuilder separator = new StringBuilder(" ");
- for (int i = 0; i < indentation; i++) {
- separator.append(" ");
- }
- //
- synchronized (this.getBuffer()) {
- this.getBuffer().write(timestamp + separator + message);
- this.getBuffer().newLine();
- this.getBuffer().flush();
- }
- }
- catch (IOException exception) {
- throw new MLoggingException("Could not write to file.", exception);
- }
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
- * See LICENSE for details.
- */
-
-package com.marcozanon.macaco.logging;
-
-import com.marcozanon.macaco.MObject;
-
-public abstract class MLogTarget extends MObject {
-
- /* */
-
- public abstract void close() throws MLoggingException;
-
- @Override
- protected void finalize() {
- try {
- this.close();
- }
- catch (Exception exception) {
- }
- }
-
- /* Output. */
-
- public abstract void appendMessage(String message) throws MLoggingException;
-
- public abstract void appendMessage(String message, int indentation) throws MLoggingException;
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
- * See LICENSE for details.
- */
-
-package com.marcozanon.macaco.logging;
-
-import com.marcozanon.macaco.MException;
-
-@SuppressWarnings("serial")
-public class MLoggingException extends MException {
-
- /* */
-
- public MLoggingException() {
- super();
- }
-
- public MLoggingException(String message) {
- super(message);
- }
-
- public MLoggingException(Throwable error) {
- super(error);
- }
-
- public MLoggingException(String message, Throwable error) {
- super(message, error);
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
- * See LICENSE for details.
- */
-
-package com.marcozanon.macaco.text;
-
-import com.marcozanon.macaco.MConstants;
-import com.marcozanon.macaco.MObject;
-import java.io.BufferedInputStream;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.util.StringJoiner;
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.parsers.SAXParser;
-import javax.xml.parsers.SAXParserFactory;
-import org.xml.sax.Attributes;
-import org.xml.sax.EntityResolver;
-import org.xml.sax.ErrorHandler;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-import org.xml.sax.SAXParseException;
-import org.xml.sax.XMLReader;
-import org.xml.sax.helpers.DefaultHandler;
-
-public class MText extends MObject {
-
- /* Checks. */
-
- public static boolean isEmpty(String x) {
- if ((null != x) && ("".equals(x))) {
- return true;
- }
- return false;
- }
-
- public static boolean isBlank(String x) {
- if ((null == x) || ("".equals(x))) {
- return true;
- }
- return false;
- }
-
- /* Utility methods. */
-
- public static String repeat(String x, int times) {
- if (1 > times) {
- throw new IllegalArgumentException(String.format("Invalid 'times': %s.", times));
- }
- //
- StringBuilder y = new StringBuilder("");
- for (int i = 0; i < times; i++) {
- y = y.append(x);
- }
- return y.toString();
- }
-
- public static String getRandomToken(int length) {
- if (0 > length) {
- throw new IllegalArgumentException(String.format("Invalid 'length': %s.", length));
- }
- //
- StringBuilder y = new StringBuilder("");
- for (int i = 0; i < length; i++) {
- char c = (char)(65 + (25 * Math.random()));
- y = y.append(c);
- }
- return y.toString();
- }
-
- public static String removeLine(String x, int lineNumber) {
- if (null == x) {
- throw new IllegalArgumentException("Invalid 'x': null.");
- }
- //
- String[] xLines = x.split(System.getProperty("line.separator"));
- //
- if ((0 > lineNumber) || (xLines.length - 1 < lineNumber)) {
- throw new IllegalArgumentException(String.format("Invalid 'lineNumber': %s.", lineNumber));
- }
- //
- StringJoiner stringJoiner = new StringJoiner(System.getProperty("line.separator"));
- for (int l = 0; xLines.length > l; l++) {
- if (l == lineNumber) {
- continue;
- }
- //
- stringJoiner.add(xLines[l]);
- }
- //
- return stringJoiner.toString();
- }
-
- public static String removeFirstLine(String x) {
- return MText.removeLine(x, 0);
- }
-
- public static String removeLastLine(String x) {
- if (null == x) {
- throw new IllegalArgumentException("Invalid 'x': null.");
- }
- //
- String[] xLines = x.split(System.getProperty("line.separator"));
- //
- return MText.removeLine(x, xLines.length - 1);
- }
-
- /* Javascript escape. */
-
- public static String getJavascriptEscapedString(String x) {
- if (null == x) {
- return null;
- }
- //
- String text = x;
- //
- text = text.replace("\\", "\\\\");
- text = text.replace("'", "\\\'");
- //
- return text;
- }
-
- /* Xhtml escape and safety. */
-
- public static String getXhtmlEscapedString(String x) { // similar to PHP's htmlspecialchars()
- if (null == x) {
- return null;
- }
- //
- String text = x;
- //
- text = text.replace("&", "&");
- text = text.replace("\"", """);
- text = text.replace("'", "'");
- text = text.replace("<", "<");
- text = text.replace(">", ">");
- //
- text = text.replace("\n", "<br />");
- //
- return text;
- }
-
- public static String getXhtmlNumericEntitiesString(String x) { // for compatibility with innerHTML
- if (null == x) {
- return null;
- }
- //
- String text = x;
- //
- text = text.replace(" ", " ");
- text = text.replace("¡", "¡");
- text = text.replace("¢", "¢");
- text = text.replace("£", "£");
- text = text.replace("¤", "¤");
- text = text.replace("¥", "¥");
- text = text.replace("¦", "¦");
- text = text.replace("§", "§");
- text = text.replace("¨", "¨");
- text = text.replace("©", "©");
- text = text.replace("ª", "ª");
- text = text.replace("«", "«");
- text = text.replace("¬", "¬");
- text = text.replace("­", "­");
- text = text.replace("®", "®");
- text = text.replace("¯", "¯");
- text = text.replace("°", "°");
- text = text.replace("±", "±");
- text = text.replace("²", "²");
- text = text.replace("³", "³");
- text = text.replace("´", "´");
- text = text.replace("µ", "µ");
- text = text.replace("¶", "¶");
- text = text.replace("·", "·");
- text = text.replace("¸", "¸");
- text = text.replace("¹", "¹");
- text = text.replace("º", "º");
- text = text.replace("»", "»");
- text = text.replace("¼", "¼");
- text = text.replace("½", "½");
- text = text.replace("¾", "¾");
- text = text.replace("¿", "¿");
- text = text.replace("À", "À");
- text = text.replace("Á", "Á");
- text = text.replace("Â", "Â");
- text = text.replace("Ã", "Ã");
- text = text.replace("Ä", "Ä");
- text = text.replace("Å", "Å");
- text = text.replace("Æ", "Æ");
- text = text.replace("Ç", "Ç");
- text = text.replace("È", "È");
- text = text.replace("É", "É");
- text = text.replace("Ê", "Ê");
- text = text.replace("Ë", "Ë");
- text = text.replace("Ì", "Ì");
- text = text.replace("Í", "Í");
- text = text.replace("Î", "Î");
- text = text.replace("Ï", "Ï");
- text = text.replace("Ð", "Ð");
- text = text.replace("Ñ", "Ñ");
- text = text.replace("Ò", "Ò");
- text = text.replace("Ó", "Ó");
- text = text.replace("Ô", "Ô");
- text = text.replace("Õ", "Õ");
- text = text.replace("Ö", "Ö");
- text = text.replace("×", "×");
- text = text.replace("Ø", "Ø");
- text = text.replace("Ù", "Ù");
- text = text.replace("Ú", "Ú");
- text = text.replace("Û", "Û");
- text = text.replace("Ü", "Ü");
- text = text.replace("Ý", "Ý");
- text = text.replace("Þ", "Þ");
- text = text.replace("ß", "ß");
- text = text.replace("à", "à");
- text = text.replace("á", "á");
- text = text.replace("â", "â");
- text = text.replace("ã", "ã");
- text = text.replace("ä", "ä");
- text = text.replace("å", "å");
- text = text.replace("æ", "æ");
- text = text.replace("ç", "ç");
- text = text.replace("è", "è");
- text = text.replace("é", "é");
- text = text.replace("ê", "ê");
- text = text.replace("ë", "ë");
- text = text.replace("ì", "ì");
- text = text.replace("í", "í");
- text = text.replace("î", "î");
- text = text.replace("ï", "ï");
- text = text.replace("ð", "ð");
- text = text.replace("ñ", "ñ");
- text = text.replace("ò", "ò");
- text = text.replace("ó", "ó");
- text = text.replace("ô", "ô");
- text = text.replace("õ", "õ");
- text = text.replace("ö", "ö");
- text = text.replace("÷", "÷");
- text = text.replace("ø", "ø");
- text = text.replace("ù", "ù");
- text = text.replace("ú", "ú");
- text = text.replace("û", "û");
- text = text.replace("ü", "ü");
- text = text.replace("ý", "ý");
- text = text.replace("þ", "þ");
- text = text.replace("ÿ", "ÿ");
- text = text.replace("Œ", "Œ");
- text = text.replace("œ", "œ");
- text = text.replace("Š", "Š");
- text = text.replace("š", "š");
- text = text.replace("Ÿ", "Ÿ");
- text = text.replace("ƒ", "ƒ");
- text = text.replace("ˆ", "ˆ");
- text = text.replace("˜", "˜");
- text = text.replace("Α", "Α");
- text = text.replace("Β", "Β");
- text = text.replace("Γ", "Γ");
- text = text.replace("Δ", "Δ");
- text = text.replace("Ε", "Ε");
- text = text.replace("Ζ", "Ζ");
- text = text.replace("Η", "Η");
- text = text.replace("Θ", "Θ");
- text = text.replace("Ι", "Ι");
- text = text.replace("Κ", "Κ");
- text = text.replace("Λ", "Λ");
- text = text.replace("Μ", "Μ");
- text = text.replace("Ν", "Ν");
- text = text.replace("Ξ", "Ξ");
- text = text.replace("Ο", "Ο");
- text = text.replace("Π", "Π");
- text = text.replace("Ρ", "Ρ");
- text = text.replace("Σ", "Σ");
- text = text.replace("Τ", "Τ");
- text = text.replace("Υ", "Υ");
- text = text.replace("Φ", "Φ");
- text = text.replace("Χ", "Χ");
- text = text.replace("Ψ", "Ψ");
- text = text.replace("Ω", "Ω");
- text = text.replace("α", "α");
- text = text.replace("β", "β");
- text = text.replace("γ", "γ");
- text = text.replace("δ", "δ");
- text = text.replace("ε", "ε");
- text = text.replace("ζ", "ζ");
- text = text.replace("η", "η");
- text = text.replace("θ", "θ");
- text = text.replace("ι", "ι");
- text = text.replace("κ", "κ");
- text = text.replace("λ", "λ");
- text = text.replace("μ", "μ");
- text = text.replace("ν", "ν");
- text = text.replace("ξ", "ξ");
- text = text.replace("ο", "ο");
- text = text.replace("π", "π");
- text = text.replace("ρ", "ρ");
- text = text.replace("ς", "ς");
- text = text.replace("σ", "σ");
- text = text.replace("τ", "τ");
- text = text.replace("υ", "υ");
- text = text.replace("φ", "φ");
- text = text.replace("χ", "χ");
- text = text.replace("ψ", "ψ");
- text = text.replace("ω", "ω");
- text = text.replace("ϑ", "ϑ");
- text = text.replace("ϒ", "ϒ");
- text = text.replace("ϖ", "ϖ");
- text = text.replace(" ", " ");
- text = text.replace(" ", " ");
- text = text.replace(" ", " ");
- text = text.replace("‌", "‌");
- text = text.replace("‍", "‍");
- text = text.replace("‎", "‎");
- text = text.replace("‏", "‏");
- text = text.replace("–", "–");
- text = text.replace("—", "—");
- text = text.replace("‘", "‘");
- text = text.replace("’", "’");
- text = text.replace("‚", "‚");
- text = text.replace("“", "“");
- text = text.replace("”", "”");
- text = text.replace("„", "„");
- text = text.replace("†", "†");
- text = text.replace("‡", "‡");
- text = text.replace("•", "•");
- text = text.replace("…", "…");
- text = text.replace("‰", "‰");
- text = text.replace("′", "′");
- text = text.replace("″", "″");
- text = text.replace("‹", "‹");
- text = text.replace("›", "›");
- text = text.replace("‾", "‾");
- text = text.replace("⁄", "⁄");
- text = text.replace("€", "€");
- text = text.replace("ℑ", "ℑ");
- text = text.replace("℘", "℘");
- text = text.replace("ℜ", "ℜ");
- text = text.replace("™", "™");
- text = text.replace("ℵ", "ℵ");
- text = text.replace("←", "←");
- text = text.replace("↑", "↑");
- text = text.replace("→", "→");
- text = text.replace("↓", "↓");
- text = text.replace("↔", "↔");
- text = text.replace("↵", "↵");
- text = text.replace("⇐", "⇐");
- text = text.replace("⇑", "⇑");
- text = text.replace("⇒", "⇒");
- text = text.replace("⇓", "⇓");
- text = text.replace("⇔", "⇔");
- text = text.replace("∀", "∀");
- text = text.replace("∂", "∂");
- text = text.replace("∃", "∃");
- text = text.replace("∅", "∅");
- text = text.replace("∇", "∇");
- text = text.replace("∈", "∈");
- text = text.replace("∉", "∉");
- text = text.replace("∋", "∋");
- text = text.replace("∏", "∏");
- text = text.replace("∑", "∑");
- text = text.replace("−", "−");
- text = text.replace("∗", "∗");
- text = text.replace("√", "√");
- text = text.replace("∝", "∝");
- text = text.replace("∞", "∞");
- text = text.replace("∠", "∠");
- text = text.replace("∧", "∧");
- text = text.replace("∨", "∨");
- text = text.replace("∩", "∩");
- text = text.replace("∪", "∪");
- text = text.replace("∫", "∫");
- text = text.replace("∴", "∴");
- text = text.replace("∼", "∼");
- text = text.replace("≅", "≅");
- text = text.replace("≈", "≈");
- text = text.replace("≠", "≠");
- text = text.replace("≡", "≡");
- text = text.replace("≤", "≤");
- text = text.replace("≥", "≥");
- text = text.replace("⊂", "⊂");
- text = text.replace("⊃", "⊃");
- text = text.replace("⊄", "⊄");
- text = text.replace("⊆", "⊆");
- text = text.replace("⊇", "⊇");
- text = text.replace("⊕", "⊕");
- text = text.replace("⊗", "⊗");
- text = text.replace("⊥", "⊥");
- text = text.replace("⋅", "⋅");
- text = text.replace("⌈", "⌈");
- text = text.replace("⌉", "⌉");
- text = text.replace("⌊", "⌊");
- text = text.replace("⌋", "⌋");
- text = text.replace("⟨", "〈");
- text = text.replace("⟩", "〉");
- text = text.replace("◊", "◊");
- text = text.replace("♠", "♠");
- text = text.replace("♣", "♣");
- text = text.replace("♥", "♥");
- text = text.replace("♦", "♦");
- //
- return text;
- }
-
- public static String getXhtmlSafeString(String x) throws MXhtmlUnsafeStringException {
- if (null == x) {
- return null;
- }
- //
- String text = x;
- //
- StringBuilder fakeXhtmlPageContent = new StringBuilder("");
- fakeXhtmlPageContent.append(String.format("<?xml version=\"1.0\" encoding=\"%s\" ?>", MConstants.TEXT_ENCODING));
- fakeXhtmlPageContent.append("<!DOCTYPE html SYSTEM \"fake-dtd\" >");
- fakeXhtmlPageContent.append("<html xmlns=\"http://www.w3.org/1999/xhtml\">");
- fakeXhtmlPageContent.append("<head><title /></head>");
- fakeXhtmlPageContent.append(String.format("<body>%s</body>", text));
- fakeXhtmlPageContent.append("</html>");
- // Validate Xhtml (with no dangerous tags and event attributes).
- try {
- SAXParserFactory factory = SAXParserFactory.newInstance();
- factory.setValidating(true);
- factory.setNamespaceAware(true);
- SAXParser parser = factory.newSAXParser();
- XMLReader reader = parser.getXMLReader();
- reader.setEntityResolver(new EntityResolver() {
-
- @Override
- public InputSource resolveEntity(String publicId, String systemId) {
- return new InputSource(new BufferedInputStream(this.getClass().getClassLoader().getResourceAsStream("dtd/xhtml1-transitional-macaco-edit.dtd")));
- }
-
- });
- reader.setContentHandler(new DefaultHandler() {
-
- @Override
- public void startElement(String namespaceUri, String strippedName, String tagName, Attributes attributes) throws SAXException {
- if ("script".equalsIgnoreCase(tagName)) {
- throw new SAXException(String.format("Tag not allowed: %s.", tagName));
- }
- for (int a = 0; attributes.getLength() > a; a++) {
- if (attributes.getLocalName(a).toLowerCase().startsWith("on")) {
- throw new SAXException(String.format("Attribute not allowed: %s.", attributes.getLocalName(a)));
- }
- }
- }
-
- });
- reader.setErrorHandler(new ErrorHandler() {
-
- @Override
- public void error(SAXParseException exception) throws SAXException {
- throw new SAXException(exception);
- }
-
- @Override
- public void fatalError(SAXParseException exception) throws SAXException {
- throw new SAXException(exception);
- }
-
- @Override
- public void warning(SAXParseException exception) throws SAXException {
- throw new SAXException(exception);
- }
-
- });
- //
- reader.parse(new InputSource(new ByteArrayInputStream(fakeXhtmlPageContent.toString().getBytes(MConstants.TEXT_ENCODING))));
- }
- catch (ParserConfigurationException exception) { // cannot happen
- }
- catch (SAXException exception) {
- throw new MXhtmlUnsafeStringException("Invalid 'x': unsafe tags or attributes inside.", exception);
- }
- catch (UnsupportedEncodingException exception) { // cannot happen
- }
- catch (IOException exception) { // cannot happen; put here not to bypass UnsupportedEncodingException
- }
- // Also convert named entities to numeric entities.
- return MText.getXhtmlNumericEntitiesString(text);
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
- * See LICENSE for details.
- */
-
-package com.marcozanon.macaco.text;
-
-import com.marcozanon.macaco.MException;
-
-public abstract class MTextException extends MException {
-
- /* */
-
- public MTextException() {
- super();
- }
-
- public MTextException(String message) {
- super(message);
- }
-
- public MTextException(Throwable error) {
- super(error);
- }
-
- public MTextException(String message, Throwable error) {
- super(message, error);
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
- * See LICENSE for details.
- */
-
-package com.marcozanon.macaco.text;
-
-public abstract class MTranslationException extends MTextException {
-
- /* */
-
- public MTranslationException() {
- super();
- }
-
- public MTranslationException(String message) {
- super(message);
- }
-
- public MTranslationException(Throwable error) {
- super(error);
- }
-
- public MTranslationException(String message, Throwable error) {
- super(message, error);
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
- * See LICENSE for details.
- */
-
-package com.marcozanon.macaco.text;
-
-@SuppressWarnings("serial")
-public class MTranslationFileParsingException extends MTranslationException {
-
- /* */
-
- public MTranslationFileParsingException() {
- super();
- }
-
- public MTranslationFileParsingException(String message) {
- super(message);
- }
-
- public MTranslationFileParsingException(Throwable error) {
- super(error);
- }
-
- public MTranslationFileParsingException(String message, Throwable error) {
- super(message, error);
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
- * See LICENSE for details.
- */
-
-package com.marcozanon.macaco.text;
-
-@SuppressWarnings("serial")
-public class MTranslationValueNotFoundException extends MTranslationException {
-
- /* */
-
- public MTranslationValueNotFoundException() {
- super();
- }
-
- public MTranslationValueNotFoundException(String message) {
- super(message);
- }
-
- public MTranslationValueNotFoundException(Throwable error) {
- super(error);
- }
-
- public MTranslationValueNotFoundException(String message, Throwable error) {
- super(message, error);
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
- * See LICENSE for details.
- */
-
-package com.marcozanon.macaco.text;
-
-import com.marcozanon.macaco.MConstants;
-import com.marcozanon.macaco.MObject;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.InputStreamReader;
-import java.io.IOException;
-import java.io.LineNumberReader;
-import java.io.UnsupportedEncodingException;
-import java.util.LinkedHashMap;
-import java.util.Locale;
-
-public class MTranslator extends MObject {
-
- protected Locale basicLocale = null;
-
- protected LinkedHashMap<String, LinkedHashMap<String, String>> messages = new LinkedHashMap<String, LinkedHashMap<String, String>>();
-
- /* */
-
- public MTranslator(Locale basicLocale) {
- super();
- //
- if (null == basicLocale) {
- throw new IllegalArgumentException("Invalid 'basicLocale': null.");
- }
- //
- this.basicLocale = basicLocale;
- }
-
- public MTranslator(Locale basicLocale, LinkedHashMap<String, LinkedHashMap<String, String>> messages) {
- this(basicLocale);
- //
- this.messages = messages;
- }
-
- public MTranslator(Locale basicLocale, String file) throws MTranslationFileParsingException {
- this(basicLocale);
- //
- this.parseFile(file);
- }
-
- @Override
- public MTranslator clone() {
- return new MTranslator(this.getBasicLocale(), this.getMessages());
- }
-
- /* Locale. */
-
- protected Locale getBasicLocale() {
- return this.basicLocale;
- }
-
- /* String management. */
-
- protected LinkedHashMap<String, LinkedHashMap<String, String>> getMessages() {
- return this.messages;
- }
-
- public void parseFile(String file) throws MTranslationFileParsingException {
- if (MText.isBlank(file)) {
- throw new IllegalArgumentException("Invalid 'file': null or empty.");
- }
- //
- try (LineNumberReader buffer = new LineNumberReader(new InputStreamReader(new FileInputStream(file), MConstants.TEXT_ENCODING))) {
- String message = null;
- String line = null;
- synchronized (this.getMessages()) {
- while (true) {
- try {
- line = buffer.readLine();
- }
- catch (IOException exception) {
- throw new MTranslationFileParsingException("Could not read file.", exception);
- }
- if (null == line) {
- break;
- }
- line = line.trim();
- if (MText.isEmpty(line)) {
- message = null;
- continue;
- }
- else if ((line.startsWith("#")) || (line.startsWith(";"))) {
- continue;
- }
- else if (null == message) {
- message = line;
- if (this.getMessages().containsKey(message)) {
- throw new MTranslationFileParsingException(String.format("Invalid line: %s: duplicated message: %s.", buffer.getLineNumber(), message));
- }
- this.getMessages().put(message, new LinkedHashMap<String, String>());
- }
- else {
- int a = line.indexOf("=");
- if (-1 == a) {
- throw new MTranslationFileParsingException(String.format("Invalid line: %s: string malformed: %s.", buffer.getLineNumber(), line));
- }
- String localeRepresentation = line.substring(0, a).trim();
- String translation = line.substring(a + 1).trim();
- if (this.getMessages().get(message).containsKey(localeRepresentation)) {
- throw new MTranslationFileParsingException(String.format("Invalid line: %s: duplicated translation for locale: %s.", buffer.getLineNumber(), message));
- }
- this.getMessages().get(message).put(localeRepresentation, translation);
- }
- }
- }
- }
- catch (FileNotFoundException exception) {
- throw new MTranslationFileParsingException("Could not open file.", exception);
- }
- catch (UnsupportedEncodingException exception) { // cannot happen
- }
- catch (IOException exception) {
- throw new MTranslationFileParsingException("Could not open / close file.", exception);
- }
- }
-
- public void clear() {
- synchronized (this.getMessages()) {
- this.getMessages().clear();
- }
- }
-
- public String t(String message, Locale locale) {
- return this.getLenientTranslation(message, locale);
- }
-
- public String getLenientTranslation(String message, Locale locale) {
- String translation = null;
- try {
- translation = this.getTranslation(message, locale, false);
- }
- catch (MTranslationValueNotFoundException exception) { // cannot happen
- }
- return translation;
- }
-
- public String getStrictTranslation(String message, Locale locale) throws MTranslationValueNotFoundException {
- return this.getTranslation(message, locale, true);
- }
-
- protected String getTranslation(String message, Locale locale, boolean strictMode) throws MTranslationValueNotFoundException {
- if (MText.isBlank(message)) {
- throw new IllegalArgumentException("Invalid 'message': null or empty.");
- }
- if (null == locale) {
- throw new IllegalArgumentException("Invalid 'locale': null.");
- }
- //
- if (!this.getMessages().containsKey(message)) {
- if (strictMode) {
- throw new MTranslationValueNotFoundException(String.format("Invalid 'message': %s: not available.", message));
- }
- return message;
- }
-/*
- if (this.getBasicLocale().equals(locale)) {
- return message;
- }
-*/
- LinkedHashMap<String, String> messageTranslations = this.getMessages().get(message);
- String localeRepresentation = locale.toString();
- if (!messageTranslations.containsKey(localeRepresentation)) {
- if (strictMode) {
- throw new MTranslationValueNotFoundException(String.format("Invalid 'locale': %s: translation not available for message: %s.", localeRepresentation, message));
- }
- String localeFallbackRepresentation = locale.getLanguage();
- if (!messageTranslations.containsKey(localeFallbackRepresentation)) {
- return message;
- }
- return messageTranslations.get(localeFallbackRepresentation);
- }
- return messageTranslations.get(localeRepresentation);
- }
-
-}
+++ /dev/null
-/**
- * Macaco
- * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
- * See LICENSE for details.
- */
-
-package com.marcozanon.macaco.text;
-
-@SuppressWarnings("serial")
-public class MXhtmlUnsafeStringException extends MTextException {
-
- /* */
-
- public MXhtmlUnsafeStringException() {
- super();
- }
-
- public MXhtmlUnsafeStringException(String message) {
- super(message);
- }
-
- public MXhtmlUnsafeStringException(Throwable error) {
- super(error);
- }
-
- public MXhtmlUnsafeStringException(String message, Throwable error) {
- super(message, error);
- }
-
-}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco;
+
+
+public class MConstants extends MObject {
+
+ public static final String MACACO_VERSION = "7.x";
+
+ public static final String TEXT_ENCODING = "UTF-8";
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco;
+
+public abstract class MException extends Exception {
+
+ /* */
+
+ public MException() {
+ super();
+ }
+
+ public MException(String message) {
+ super(message);
+ }
+
+ public MException(Throwable error) {
+ super(error);
+ }
+
+ public MException(String message, Throwable error) {
+ super(message, error);
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+public class MInformation extends MObject {
+
+ /* Generic information. */
+
+ public static String getMacacoVersion() {
+ return MConstants.MACACO_VERSION;
+ }
+
+ public static String getMacacoFullName() {
+ return "Macaco " + MInformation.getMacacoVersion();
+ }
+
+ public static String getMacacoCopyrightInformation() {
+ StringBuilder s = new StringBuilder("");
+ s.append(MInformation.getMacacoFullName() + System.getProperty("line.separator"));
+ s.append("Copyright (c) 2009-2022 by Marco Zanon." + System.getProperty("line.separator"));
+ s.append("Released under MIT license (see LICENSE.MIT.sample for details)." + System.getProperty("line.separator"));
+ s.append("Small portions inspired by other projects or web pages. See source code for additional information.");
+ return s.toString();
+ }
+
+ /* Throwables. */
+
+ public static String getThrowableAsString(Throwable throwable) {
+ if (null == throwable) {
+ throw new IllegalArgumentException("Invalid 'throwable': null.");
+ }
+ //
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ throwable.printStackTrace(pw);
+ pw.flush();
+ sw.flush();
+ //
+ return sw.toString();
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco;
+
+public abstract class MObject {
+
+ /* */
+
+ @Override
+ protected MObject clone() {
+ throw new UnsupportedOperationException("Please provide manually by yourself.");
+ }
+
+ @Override
+ public String toString() {
+ throw new UnsupportedOperationException("Please use appropriate methods (if any).");
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco.conversion;
+
+import com.marcozanon.macaco.MException;
+
+public abstract class MConversionException extends MException {
+
+ /* */
+
+ public MConversionException() {
+ super();
+ }
+
+ public MConversionException(String message) {
+ super(message);
+ }
+
+ public MConversionException(Throwable error) {
+ super(error);
+ }
+
+ public MConversionException(String message, Throwable error) {
+ super(message, error);
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco.conversion;
+
+import com.marcozanon.macaco.MObject;
+import com.marcozanon.macaco.text.MText;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.LinkedHashSet;
+import java.util.Locale;
+import java.util.TimeZone;
+
+public class MDateConverter extends MObject {
+
+ protected LinkedHashSet<String> dateFormats = new LinkedHashSet<String>();
+ protected Locale locale = null;
+ protected TimeZone timeZone = null;
+
+ /* */
+
+ public MDateConverter(String defaultDateFormat, Locale locale) {
+ this(defaultDateFormat, locale, TimeZone.getDefault());
+ }
+
+ public MDateConverter(String defaultDateFormat, Locale locale, TimeZone timeZone) {
+ super();
+ //
+ this.addDateFormat(defaultDateFormat);
+ this.setLocale(locale);
+ this.setTimeZone(timeZone);
+ }
+
+ public MDateConverter(LinkedHashSet<String> dateFormats, Locale locale, TimeZone timeZone) {
+ super();
+ //
+ this.setDateFormats(dateFormats);
+ this.setLocale(locale);
+ this.setTimeZone(timeZone);
+ }
+
+ @Override
+ public MDateConverter clone() {
+ return new MDateConverter(this.getDateFormats(), this.getLocale(), this.getTimeZone());
+ }
+
+ /* Date formats. */
+
+ public void setDateFormats(LinkedHashSet<String> dateFormats) {
+ if (null == dateFormats) {
+ throw new IllegalArgumentException("Invalid 'dateFormats': null.");
+ }
+ else if (0 == dateFormats.size()) {
+ throw new IllegalArgumentException("Invalid 'dateFormats': empty.");
+ }
+ else {
+ for (String dateFormat: dateFormats) {
+ MDateConverter.checkDateFormat(dateFormat);
+ }
+ }
+ //
+ synchronized (this.dateFormats) {
+ this.dateFormats = dateFormats;
+ }
+ }
+
+ public void addDateFormat(String dateFormat) {
+ MDateConverter.checkDateFormat(dateFormat);
+ //
+ synchronized (this.dateFormats) {
+ this.dateFormats.add(dateFormat);
+ }
+ }
+
+ public LinkedHashSet<String> getDateFormats() {
+ return this.dateFormats;
+ }
+
+ public String getDefaultDateFormat() {
+ return this.getDateFormats().iterator().next();
+ }
+
+ /* Locale. */
+
+ protected void setLocale(Locale locale) {
+ if (null == locale) {
+ throw new IllegalArgumentException("Invalid 'locale': null.");
+ }
+ //
+ this.locale = locale;
+ }
+
+ public Locale getLocale() {
+ return this.locale;
+ }
+
+ /* Time zone. */
+
+ protected void setTimeZone(TimeZone timeZone) {
+ if (null == timeZone) {
+ throw new IllegalArgumentException("Invalid 'timeZone': null.");
+ }
+ //
+ this.timeZone = timeZone;
+ }
+
+ public TimeZone getTimeZone() {
+ return this.timeZone;
+ }
+
+ /* Conversions. */
+
+ protected static void checkDateFormat(String dateFormat) {
+ if (MText.isBlank(dateFormat)) {
+ throw new IllegalArgumentException("Invalid 'dateFormat': null or empty.");
+ }
+ //
+ try {
+ SimpleDateFormat testDateFormat = new SimpleDateFormat(dateFormat);
+ }
+ catch (IllegalArgumentException exception) {
+ throw new IllegalArgumentException(String.format("Invalid 'dateFormat': %s.", dateFormat)); // no need to propagate exception
+ }
+ }
+
+ protected static Date getDateFromStringByParameters(String x, String inputDateFormat, Locale inputLocale, TimeZone inputTimeZone) throws MInvalidConversionFormatException {
+ if (MText.isBlank(x)) {
+ throw new IllegalArgumentException("Invalid 'x': null or empty.");
+ }
+ MDateConverter.checkDateFormat(inputDateFormat);
+ if (null == inputLocale) {
+ throw new IllegalArgumentException("Invalid 'inputLocale': null.");
+ }
+ if (null == inputTimeZone) {
+ throw new IllegalArgumentException("Invalid 'inputTimeZone': null.");
+ }
+ //
+ Calendar c1 = Calendar.getInstance(inputTimeZone, inputLocale);
+ c1.clear();
+ c1.setLenient(false);
+ SimpleDateFormat sdf = new SimpleDateFormat(inputDateFormat, inputLocale);
+ sdf.setCalendar(c1);
+ Date d1 = null;
+ try {
+ d1 = sdf.parse(x);
+ }
+ catch (ParseException exception) {
+ throw new MInvalidConversionFormatException(String.format("Invalid 'x' or parsing: %s (input format: %s).", x, inputDateFormat)); // no need to propagate exception
+ }
+ Calendar c2 = Calendar.getInstance(inputTimeZone, inputLocale);
+ c2.clear();
+ c2.set(Calendar.YEAR, c1.get(Calendar.YEAR));
+ c2.set(Calendar.MONTH, c1.get(Calendar.MONTH));
+ c2.set(Calendar.DAY_OF_MONTH, c1.get(Calendar.DAY_OF_MONTH));
+ c2.set(Calendar.HOUR_OF_DAY, c1.get(Calendar.HOUR_OF_DAY));
+ c2.set(Calendar.MINUTE, c1.get(Calendar.MINUTE));
+ c2.set(Calendar.SECOND, c1.get(Calendar.SECOND));
+ Date d2 = c2.getTime();
+ if (!x.equals(sdf.format(d2))) {
+ throw new MInvalidConversionFormatException(String.format("Invalid 'x' or parsing: %s (input format: %s).", x, inputDateFormat));
+ }
+ return d2;
+ }
+
+ protected static String getStringFromDateByParameters(Date date, String outputDateFormat, Locale outputLocale, TimeZone outputTimeZone) {
+ if (null == date) {
+ throw new IllegalArgumentException("Invalid 'date': null.");
+ }
+ MDateConverter.checkDateFormat(outputDateFormat);
+ if (null == outputLocale) {
+ throw new IllegalArgumentException("Invalid 'outputLocale': null.");
+ }
+ if (null == outputTimeZone) {
+ throw new IllegalArgumentException("Invalid 'outputTimeZone': null.");
+ }
+ //
+ SimpleDateFormat sdf = new SimpleDateFormat(outputDateFormat, outputLocale);
+ sdf.setCalendar(Calendar.getInstance(outputTimeZone, outputLocale));
+ return sdf.format(date);
+ }
+
+ public Date getDateFromString(String x) throws MInvalidConversionFormatException {
+ Date y = null;
+ for (String dateFormat: this.getDateFormats()) {
+ try {
+ y = MDateConverter.getDateFromStringByParameters(x, dateFormat, this.getLocale(), this.getTimeZone());
+ return y;
+ }
+ catch (MInvalidConversionFormatException exception) {
+ }
+ }
+ if (null == y) {
+ throw new MInvalidConversionFormatException(String.format("Invalid 'x': %s.", x));
+ }
+ return y; // necessary to avoid Java compilation errors
+ }
+
+ public String getStringFromDate(Date x) {
+ return MDateConverter.getStringFromDateByParameters(x, this.getDefaultDateFormat(), this.getLocale(), this.getTimeZone());
+ }
+
+ /* Helpers. */
+
+ public static Date getFlatDate(Date x) {
+ if (null == x) {
+ return x;
+ }
+ //
+ Calendar calendar = new GregorianCalendar();
+ calendar.setTime(x);
+ calendar.set(Calendar.HOUR_OF_DAY, 0);
+ calendar.set(Calendar.MINUTE, 0);
+ calendar.set(Calendar.SECOND, 0);
+ calendar.set(Calendar.MILLISECOND, 0);
+ return calendar.getTime();
+ }
+
+ public static Date getCeilDate(Date x) {
+ if (null == x) {
+ return x;
+ }
+ //
+ Calendar calendar = new GregorianCalendar();
+ calendar.setTime(x);
+ calendar.set(Calendar.HOUR_OF_DAY, 23);
+ calendar.set(Calendar.MINUTE, 59);
+ calendar.set(Calendar.SECOND, 59);
+ calendar.set(Calendar.MILLISECOND, 999);
+ return calendar.getTime();
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco.conversion;
+
+@SuppressWarnings("serial")
+public class MInvalidConversionFormatException extends MConversionException {
+
+ /* */
+
+ public MInvalidConversionFormatException() {
+ super();
+ }
+
+ public MInvalidConversionFormatException(String message) {
+ super(message);
+ }
+
+ public MInvalidConversionFormatException(Throwable error) {
+ super(error);
+ }
+
+ public MInvalidConversionFormatException(String message, Throwable error) {
+ super(message, error);
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco.conversion;
+
+import com.marcozanon.macaco.MObject;
+import com.marcozanon.macaco.text.MText;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.time.format.ResolverStyle;
+import java.util.LinkedHashSet;
+import java.util.Locale;
+
+public class MLocalDateConverter extends MObject {
+
+ protected LinkedHashSet<String> dateFormats = new LinkedHashSet<String>();
+ protected Locale locale = null;
+
+ /* */
+
+ public MLocalDateConverter(String defaultDateFormat, Locale locale) {
+ super();
+ //
+ this.addDateFormat(defaultDateFormat);
+ this.setLocale(locale);
+ }
+
+ public MLocalDateConverter(LinkedHashSet<String> dateFormats, Locale locale) {
+ super();
+ //
+ this.setDateFormats(dateFormats);
+ this.setLocale(locale);
+ }
+
+ @Override
+ public MLocalDateConverter clone() {
+ return new MLocalDateConverter(this.getDateFormats(), this.getLocale());
+ }
+
+ /* Date formats. */
+
+ public void setDateFormats(LinkedHashSet<String> dateFormats) {
+ if (null == dateFormats) {
+ throw new IllegalArgumentException("Invalid 'dateFormats': null.");
+ }
+ else if (0 == dateFormats.size()) {
+ throw new IllegalArgumentException("Invalid 'dateFormats': empty.");
+ }
+ else {
+ for (String dateFormat: dateFormats) {
+ MLocalDateConverter.checkDateFormat(dateFormat);
+ }
+ }
+ //
+ synchronized (this.dateFormats) {
+ this.dateFormats = dateFormats;
+ }
+ }
+
+ public void addDateFormat(String dateFormat) {
+ MLocalDateConverter.checkDateFormat(dateFormat);
+ //
+ synchronized (this.dateFormats) {
+ this.dateFormats.add(dateFormat);
+ }
+ }
+
+ public LinkedHashSet<String> getDateFormats() {
+ return this.dateFormats;
+ }
+
+ public String getDefaultDateFormat() {
+ return this.getDateFormats().iterator().next();
+ }
+
+ /* Locale. */
+
+ protected void setLocale(Locale locale) {
+ if (null == locale) {
+ throw new IllegalArgumentException("Invalid 'locale': null.");
+ }
+ //
+ this.locale = locale;
+ }
+
+ public Locale getLocale() {
+ return this.locale;
+ }
+
+ /* Conversions. */
+
+ protected static void checkDateFormat(String dateFormat) {
+ if (MText.isBlank(dateFormat)) {
+ throw new IllegalArgumentException("Invalid 'dateFormat': null or empty.");
+ }
+ //
+ try {
+ DateTimeFormatter.ofPattern(dateFormat).withResolverStyle(ResolverStyle.STRICT);
+ }
+ catch (IllegalArgumentException exception) {
+ throw new IllegalArgumentException(String.format("Invalid 'dateFormat': %s.", dateFormat)); // no need to propagate exception
+ }
+ }
+
+ protected static LocalDate getDateFromStringByParameters(String x, String inputDateFormat, Locale inputLocale) throws MInvalidConversionFormatException {
+ if (MText.isBlank(x)) {
+ throw new IllegalArgumentException("Invalid 'x': null or empty.");
+ }
+ MLocalDateConverter.checkDateFormat(inputDateFormat);
+ if (null == inputLocale) {
+ throw new IllegalArgumentException("Invalid 'inputLocale': null.");
+ }
+ //
+ LocalDate d = null;
+ try {
+ d = LocalDate.parse(x, DateTimeFormatter.ofPattern(inputDateFormat, inputLocale).withResolverStyle(ResolverStyle.STRICT));
+ }
+ catch (DateTimeParseException exception) {
+ throw new MInvalidConversionFormatException(String.format("Invalid 'x' or parsing: %s (input format: %s).", x, inputDateFormat)); // no need to propagate exception
+ }
+ //
+ return d;
+ }
+
+ protected static String getStringFromDateByParameters(LocalDate date, String outputDateFormat, Locale outputLocale) {
+ if (null == date) {
+ throw new IllegalArgumentException("Invalid 'date': null.");
+ }
+ MLocalDateConverter.checkDateFormat(outputDateFormat);
+ if (null == outputLocale) {
+ throw new IllegalArgumentException("Invalid 'outputLocale': null.");
+ }
+ //
+ return date.format(DateTimeFormatter.ofPattern(outputDateFormat, outputLocale).withResolverStyle(ResolverStyle.STRICT));
+ }
+
+ public LocalDate getDateFromString(String x) throws MInvalidConversionFormatException {
+ LocalDate y = null;
+ for (String dateFormat: this.getDateFormats()) {
+ try {
+ y = MLocalDateConverter.getDateFromStringByParameters(x, dateFormat, this.getLocale());
+ return y;
+ }
+ catch (MInvalidConversionFormatException exception) {
+ }
+ }
+ if (null == y) {
+ throw new MInvalidConversionFormatException(String.format("Invalid 'x': %s.", x));
+ }
+ return y; // necessary to avoid Java compilation errors
+ }
+
+ public String getStringFromDate(LocalDate x) {
+ return MLocalDateConverter.getStringFromDateByParameters(x, this.getDefaultDateFormat(), this.getLocale());
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco.conversion;
+
+import com.marcozanon.macaco.MObject;
+import com.marcozanon.macaco.text.MText;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.time.format.ResolverStyle;
+import java.util.LinkedHashSet;
+import java.util.Locale;
+
+public class MLocalDateTimeConverter extends MObject {
+
+ protected LinkedHashSet<String> datetimeFormats = new LinkedHashSet<String>();
+ protected Locale locale = null;
+
+ /* */
+
+ public MLocalDateTimeConverter(String defaultDatetimeFormat, Locale locale) {
+ super();
+ //
+ this.addDatetimeFormat(defaultDatetimeFormat);
+ this.setLocale(locale);
+ }
+
+ public MLocalDateTimeConverter(LinkedHashSet<String> datetimeFormats, Locale locale) {
+ super();
+ //
+ this.setDatetimeFormats(datetimeFormats);
+ this.setLocale(locale);
+ }
+
+ @Override
+ public MLocalDateTimeConverter clone() {
+ return new MLocalDateTimeConverter(this.getDatetimeFormats(), this.getLocale());
+ }
+
+ /* Datetime formats. */
+
+ public void setDatetimeFormats(LinkedHashSet<String> datetimeFormats) {
+ if (null == datetimeFormats) {
+ throw new IllegalArgumentException("Invalid 'datetimeFormats': null.");
+ }
+ else if (0 == datetimeFormats.size()) {
+ throw new IllegalArgumentException("Invalid 'datetimeFormats': empty.");
+ }
+ else {
+ for (String datetimeFormat: datetimeFormats) {
+ MLocalDateTimeConverter.checkDatetimeFormat(datetimeFormat);
+ }
+ }
+ //
+ synchronized (this.datetimeFormats) {
+ this.datetimeFormats = datetimeFormats;
+ }
+ }
+
+ public void addDatetimeFormat(String datetimeFormat) {
+ MLocalDateTimeConverter.checkDatetimeFormat(datetimeFormat);
+ //
+ synchronized (this.datetimeFormats) {
+ this.datetimeFormats.add(datetimeFormat);
+ }
+ }
+
+ public LinkedHashSet<String> getDatetimeFormats() {
+ return this.datetimeFormats;
+ }
+
+ public String getDefaultDatetimeFormat() {
+ return this.getDatetimeFormats().iterator().next();
+ }
+
+ /* Locale. */
+
+ protected void setLocale(Locale locale) {
+ if (null == locale) {
+ throw new IllegalArgumentException("Invalid 'locale': null.");
+ }
+ //
+ this.locale = locale;
+ }
+
+ public Locale getLocale() {
+ return this.locale;
+ }
+
+ /* Conversions. */
+
+ protected static void checkDatetimeFormat(String datetimeFormat) {
+ if (MText.isBlank(datetimeFormat)) {
+ throw new IllegalArgumentException("Invalid 'datetimeFormat': null or empty.");
+ }
+ //
+ try {
+ DateTimeFormatter.ofPattern(datetimeFormat).withResolverStyle(ResolverStyle.STRICT);
+ }
+ catch (IllegalArgumentException exception) {
+ throw new IllegalArgumentException(String.format("Invalid 'datetimeFormat': %s.", datetimeFormat)); // no need to propagate exception
+ }
+ }
+
+ protected static LocalDateTime getDatetimeFromStringByParameters(String x, String inputDatetimeFormat, Locale inputLocale) throws MInvalidConversionFormatException {
+ if (MText.isBlank(x)) {
+ throw new IllegalArgumentException("Invalid 'x': null or empty.");
+ }
+ MLocalDateTimeConverter.checkDatetimeFormat(inputDatetimeFormat);
+ if (null == inputLocale) {
+ throw new IllegalArgumentException("Invalid 'inputLocale': null.");
+ }
+ //
+ LocalDateTime dt = null;
+ try {
+ dt = LocalDateTime.parse(x, DateTimeFormatter.ofPattern(inputDatetimeFormat, inputLocale).withResolverStyle(ResolverStyle.STRICT));
+ }
+ catch (DateTimeParseException exception) {
+ throw new MInvalidConversionFormatException(String.format("Invalid 'x' or parsing: %s (input format: %s).", x, inputDatetimeFormat)); // no need to propagate exception
+ }
+ //
+ return dt;
+ }
+
+ protected static String getStringFromDatetimeByParameters(LocalDateTime datetime, String outputDatetimeFormat, Locale outputLocale) {
+ if (null == datetime) {
+ throw new IllegalArgumentException("Invalid 'datetime': null.");
+ }
+ MLocalDateTimeConverter.checkDatetimeFormat(outputDatetimeFormat);
+ if (null == outputLocale) {
+ throw new IllegalArgumentException("Invalid 'outputLocale': null.");
+ }
+ //
+ return datetime.format(DateTimeFormatter.ofPattern(outputDatetimeFormat, outputLocale).withResolverStyle(ResolverStyle.STRICT));
+ }
+
+ public LocalDateTime getDatetimeFromString(String x) throws MInvalidConversionFormatException {
+ LocalDateTime y = null;
+ for (String datetimeFormat: this.getDatetimeFormats()) {
+ try {
+ y = MLocalDateTimeConverter.getDatetimeFromStringByParameters(x, datetimeFormat, this.getLocale());
+ return y;
+ }
+ catch (MInvalidConversionFormatException exception) {
+ }
+ }
+ if (null == y) {
+ throw new MInvalidConversionFormatException(String.format("Invalid 'x': %s.", x));
+ }
+ return y; // necessary to avoid Java compilation errors
+ }
+
+ public String getStringFromDatetime(LocalDateTime x) {
+ return MLocalDateTimeConverter.getStringFromDatetimeByParameters(x, this.getDefaultDatetimeFormat(), this.getLocale());
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco.conversion;
+
+import com.marcozanon.macaco.MObject;
+import com.marcozanon.macaco.text.MText;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.text.ParsePosition;
+import java.util.LinkedHashSet;
+import java.util.Locale;
+
+public class MNumberConverter extends MObject {
+
+ protected LinkedHashSet<String> numberFormats = new LinkedHashSet<String>();
+ protected Locale locale = null;
+
+ /* */
+
+ public MNumberConverter(String defaultNumberFormat, Locale locale) {
+ super();
+ //
+ this.addNumberFormat(defaultNumberFormat);
+ this.setLocale(locale);
+ }
+
+ public MNumberConverter(LinkedHashSet<String> numberFormats, Locale locale) {
+ super();
+ //
+ this.setNumberFormats(numberFormats);
+ this.setLocale(locale);
+ }
+
+ @Override
+ public MNumberConverter clone() {
+ return new MNumberConverter(this.getNumberFormats(), this.getLocale());
+ }
+
+ /* Number formats. */
+
+ public void setNumberFormats(LinkedHashSet<String> numberFormats) {
+ if (null == numberFormats) {
+ throw new IllegalArgumentException("Invalid 'numberFormats': null.");
+ }
+ else if (0 == numberFormats.size()) {
+ throw new IllegalArgumentException("Invalid 'numberFormats': empty.");
+ }
+ else {
+ for (String numberFormat: numberFormats) {
+ MNumberConverter.checkNumberFormat(numberFormat);
+ }
+ }
+ //
+ synchronized (this.numberFormats) {
+ this.numberFormats = numberFormats;
+ }
+ }
+
+ public void addNumberFormat(String numberFormat) {
+ MNumberConverter.checkNumberFormat(numberFormat);
+ //
+ synchronized (this.numberFormats) {
+ this.numberFormats.add(numberFormat);
+ }
+ }
+
+ public LinkedHashSet<String> getNumberFormats() {
+ return this.numberFormats;
+ }
+
+ public String getDefaultNumberFormat() {
+ return this.getNumberFormats().iterator().next();
+ }
+
+ /* Locale. */
+
+ protected void setLocale(Locale locale) {
+ if (null == locale) {
+ throw new IllegalArgumentException("Invalid 'locale': null.");
+ }
+ //
+ this.locale = locale;
+ }
+
+ public Locale getLocale() {
+ return this.locale;
+ }
+
+ /* Conversions. */
+
+ protected static void checkNumberFormat(String numberFormat) {
+ if (MText.isBlank(numberFormat)) {
+ throw new IllegalArgumentException("Invalid 'numberFormat': null or empty.");
+ }
+ //
+ try {
+ DecimalFormat testNumberFormat = new DecimalFormat(numberFormat);
+ }
+ catch (IllegalArgumentException exception) {
+ throw new IllegalArgumentException(String.format("Invalid 'numberFormat': %s.", numberFormat)); // no need to propagate exception
+ }
+ }
+
+ protected static BigDecimal getNumberFromStringByParameters(String x, String inputNumberFormat, Locale inputLocale) throws MInvalidConversionFormatException {
+ if (MText.isBlank(x)) {
+ throw new IllegalArgumentException("Invalid 'x': null or empty.");
+ }
+ MNumberConverter.checkNumberFormat(inputNumberFormat);
+ if (null == inputLocale) {
+ throw new IllegalArgumentException("Invalid 'inputLocale': null.");
+ }
+ //
+ DecimalFormatSymbols dfs = new DecimalFormatSymbols(inputLocale);
+ DecimalFormat df = new DecimalFormat(inputNumberFormat, dfs);
+ df.setParseBigDecimal(true);
+ ParsePosition validPosition = new ParsePosition(0);
+ BigDecimal bd = (BigDecimal)df.parse(x, validPosition);
+ if (validPosition.getIndex() < x.length()) {
+ throw new MInvalidConversionFormatException(String.format("Invalid 'x' or parsing: %s (input format: %s).", x, inputNumberFormat));
+ }
+ return bd;
+ }
+
+ protected static String getStringFromNumberByParameters(BigDecimal number, String outputNumberFormat, Locale outputLocale) {
+ if (null == number) {
+ throw new IllegalArgumentException("Invalid 'number': null.");
+ }
+ MNumberConverter.checkNumberFormat(outputNumberFormat);
+ if (null == outputLocale) {
+ throw new IllegalArgumentException("Invalid 'outputLocale': null.");
+ }
+ //
+ DecimalFormatSymbols dfs = new DecimalFormatSymbols(outputLocale);
+ DecimalFormat df = new DecimalFormat(outputNumberFormat, dfs);
+ df.setRoundingMode(RoundingMode.HALF_UP);
+ return df.format(number);
+ }
+
+ public BigDecimal getNumberFromString(String x) throws MInvalidConversionFormatException {
+ BigDecimal y = null;
+ for (String numberFormat: this.getNumberFormats()) {
+ try {
+ y = MNumberConverter.getNumberFromStringByParameters(x, numberFormat, this.getLocale());
+ return y;
+ }
+ catch (MInvalidConversionFormatException exception) {
+ }
+ }
+ if (null == y) {
+ throw new MInvalidConversionFormatException(String.format("Invalid 'x': %s.", x));
+ }
+ return y; // necessary to avoid Java compilation errors
+ }
+
+ public String getStringFromNumber(BigDecimal x) {
+ return MNumberConverter.getStringFromNumberByParameters(x, this.getDefaultNumberFormat(), this.getLocale());
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco.database;
+
+import com.marcozanon.macaco.MObject;
+import com.marcozanon.macaco.logging.MLogListener;
+import com.marcozanon.macaco.text.MText;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+
+public class MDatabaseConnection extends MObject {
+
+ public static enum TransactionStatus {
+ CLOSED,
+ SUCCESSFUL,
+ FAILED
+ };
+
+ protected String driver = null;
+ protected String url = null;
+ protected String username = null;
+ protected String password = null;
+ protected boolean localTypesMode = false;
+ protected MLogListener logListener = null;
+
+ protected Connection connection = null;
+
+ protected MDatabaseConnection.TransactionStatus transactionStatus = MDatabaseConnection.TransactionStatus.CLOSED;
+
+ /* */
+
+ public MDatabaseConnection(String driver, String url, String username, String password, boolean localTypesMode, MLogListener logListener) throws MDatabaseConnectionFailureException {
+ super();
+ //
+ if (MText.isBlank(driver)) {
+ throw new IllegalArgumentException("Invalid 'driver': null or empty.");
+ }
+ if (MText.isBlank(url)) {
+ throw new IllegalArgumentException("Invalid 'url': null or empty.");
+ }
+ if (null == username) {
+ throw new IllegalArgumentException("Invalid 'username': null.");
+ }
+ if (null == password) {
+ throw new IllegalArgumentException("Invalid 'password': null.");
+ }
+ //
+ this.driver = driver;
+ this.url = url;
+ this.username = username;
+ this.password = password;
+ this.localTypesMode = localTypesMode;
+ this.setLogListener(logListener);
+ // Load driver.
+ try {
+ Class.forName(this.getDriver()).newInstance();
+ }
+ catch (ClassNotFoundException exception) {
+ throw new MDatabaseConnectionFailureException("Could not load driver.", exception);
+ }
+ catch (IllegalAccessException exception) {
+ throw new MDatabaseConnectionFailureException("Could not load driver.", exception);
+ }
+ catch (InstantiationException exception) {
+ throw new MDatabaseConnectionFailureException("Could not load driver.", exception);
+ }
+ // Initialize the connection.
+ this.initialize();
+ }
+
+ public void initialize() throws MDatabaseConnectionFailureException {
+ // Establish a connection.
+ try {
+ this.connection = DriverManager.getConnection(this.getUrl(), this.getUsername(), this.getPassword());
+ }
+ catch (SQLException exception) {
+ throw new MDatabaseConnectionFailureException("Could not get database connection.", exception);
+ }
+ // Set SQL mode to standard ANSI and TRADITIONAL.
+ try {
+ this.getConnection().createStatement().executeUpdate("SET SQL_MODE = 'ANSI,TRADITIONAL'");
+/* Disabled to prevent an infinite loop.
+ this.logStatement("### SET SQL_MODE = 'ANSI,TRADITIONAL' ###");
+*/
+ }
+ catch (SQLException exception) {
+ throw new MDatabaseConnectionFailureException("Could not set SQL mode to ANSI and TRADITIONAL.", exception);
+ }
+ }
+
+ protected void check() throws MDatabaseConnectionFailureException {
+ try {
+ this.getConnection().createStatement().executeQuery("/* ping */ SELECT 1");
+ }
+ catch (SQLException exception) {
+ this.initialize();
+ }
+ }
+
+ public void close() throws MDatabaseConnectionFailureException {
+ try {
+ this.getConnection().close();
+ }
+ catch (SQLException exception) {
+ throw new MDatabaseConnectionFailureException("Could not close database connection.", exception);
+ }
+ }
+
+ public boolean isClosed() throws MDatabaseConnectionFailureException {
+ try {
+ return this.getConnection().isClosed();
+ }
+ catch (SQLException exception) {
+ throw new MDatabaseConnectionFailureException("Could not determine the state.", exception);
+ }
+ }
+
+ @Override
+ protected void finalize() {
+ try {
+ this.close();
+ }
+ catch (Exception exception) {
+ }
+ }
+
+ /* Driver. */
+
+ public String getDriver() {
+ return this.driver;
+ }
+
+ /* Url. */
+
+ public String getUrl() {
+ return this.url;
+ }
+
+ /* Username. */
+
+ public String getUsername() {
+ return this.username;
+ }
+
+ /* Password. */
+
+ public String getPassword() {
+ return this.password;
+ }
+
+ /* Local types mode. */
+
+ public boolean getLocalTypesMode() {
+ return this.localTypesMode;
+ }
+
+ /* Connection. */
+
+ protected Connection getConnection() {
+ return this.connection;
+ }
+
+ /* Transactions. */
+
+ protected void setTransactionStatus(MDatabaseConnection.TransactionStatus transactionStatus) {
+ if (null == transactionStatus) {
+ throw new IllegalArgumentException("Invalid 'transactionStatus': null.");
+ }
+ //
+ this.transactionStatus = transactionStatus;
+ }
+
+ public MDatabaseConnection.TransactionStatus getTransactionStatus() {
+ return this.transactionStatus;
+ }
+
+ public void startTransaction() throws MSqlTransactionException {
+ if (MDatabaseConnection.TransactionStatus.CLOSED != this.getTransactionStatus()) {
+ throw new MSqlTransactionException("Nested transactions not allowed.");
+ }
+ //
+ try {
+ // Check the connection.
+ this.check();
+ // Start the transaction.
+ this.getConnection().setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
+ this.getConnection().setAutoCommit(false);
+ this.setTransactionStatus(MDatabaseConnection.TransactionStatus.SUCCESSFUL);
+ this.logStatement("### BEGIN TRANSACTION ###");
+ }
+ catch (MDatabaseConnectionFailureException exception) {
+ throw new MSqlTransactionException("Could not start transaction.", exception);
+ }
+ catch (SQLException exception) {
+ throw new MSqlTransactionException("Could not start transaction.", exception);
+ }
+ }
+
+ public void rollBackTransaction() throws MSqlTransactionException {
+ if (MDatabaseConnection.TransactionStatus.CLOSED == this.getTransactionStatus()) {
+ throw new MSqlTransactionException("Transaction not started.");
+ }
+ //
+ try {
+/*
+ // Check the connection.
+ this.check();
+*/
+ // Cancel the transaction.
+ this.getConnection().rollback();
+ this.getConnection().setAutoCommit(true);
+ this.setTransactionStatus(MDatabaseConnection.TransactionStatus.CLOSED);
+ this.logStatement("### ROLLBACK ###");
+ }
+ catch (SQLException exception) {
+ this.setTransactionStatus(MDatabaseConnection.TransactionStatus.FAILED);
+ throw new MSqlTransactionException("Could not roll back transaction.", exception);
+ }
+ }
+
+ public MDatabaseConnection.TransactionStatus commitTransaction() throws MSqlTransactionException {
+ switch (this.getTransactionStatus()) {
+ case SUCCESSFUL:
+ try {
+/*
+ // Check the connection.
+ this.check();
+*/
+ // Commit the transaction.
+ this.getConnection().commit();
+ this.getConnection().setAutoCommit(true);
+ this.setTransactionStatus(MDatabaseConnection.TransactionStatus.CLOSED);
+ this.logStatement("### COMMIT ###");
+ return MDatabaseConnection.TransactionStatus.SUCCESSFUL;
+ }
+ catch (SQLException exception) {
+ this.rollBackTransaction();
+ return MDatabaseConnection.TransactionStatus.FAILED;
+ }
+ case FAILED:
+ this.rollBackTransaction();
+ return MDatabaseConnection.TransactionStatus.FAILED;
+ default: // instead of "case CLOSED:" (necessary to avoid Java compilation errors)
+ throw new MSqlTransactionException("Transaction not started.");
+ }
+ }
+
+ /* Statements. */
+
+ public MSqlStatementResults executePreparedStatement(String statement) throws MDatabaseConnectionFailureException, MSqlStatementException {
+ return this.executePreparedStatement(statement, new LinkedList<Object>());
+ }
+
+ public MSqlStatementResults executePreparedStatement(String statement, LinkedList<Object> parameters) throws MDatabaseConnectionFailureException, MSqlStatementException {
+ return this.executePreparedStatement(statement, parameters, this.getLocalTypesMode());
+ }
+
+ public MSqlStatementResults executePreparedStatement(String statement, LinkedList<Object> parameters, boolean localTypesMode) throws MDatabaseConnectionFailureException, MSqlStatementException {
+ return this.executePreparedStatement(statement, parameters, this.getLocalTypesMode(), true);
+ };
+
+ public MSqlStatementResults executePreparedStatement(String statement, LinkedList<Object> parameters, boolean localTypesMode, boolean loggableStatement) throws MDatabaseConnectionFailureException, MSqlStatementException {
+ if (MText.isBlank(statement)) {
+ throw new IllegalArgumentException("Invalid 'statement': null or empty.");
+ }
+ if (null == parameters) {
+ throw new IllegalArgumentException("Invalid 'parameters': null.");
+ }
+ //
+ MSqlStatementResults results = null;
+ PreparedStatement preparedStatement = null;
+ try {
+ // Check the connection.
+ this.check();
+ // Prepare the statement.
+ preparedStatement = this.getConnection().prepareStatement(statement, PreparedStatement.RETURN_GENERATED_KEYS);
+ for (int p = 0; parameters.size() > p; p++) {
+ preparedStatement.setObject(p + 1, parameters.get(p));
+ }
+ // Execute the statement and parse the results.
+ preparedStatement.execute();
+ results = new MSqlStatementResults(preparedStatement, localTypesMode);
+ if (loggableStatement) {
+ this.logStatement(preparedStatement.toString());
+ }
+ }
+ catch (MDatabaseConnectionFailureException exception) {
+ if (MDatabaseConnection.TransactionStatus.SUCCESSFUL == this.getTransactionStatus()) {
+ this.setTransactionStatus(MDatabaseConnection.TransactionStatus.FAILED);
+ }
+ throw new MSqlStatementException(String.format("Could not execute prepared statement: %s.", preparedStatement), exception);
+ }
+ catch (SQLException exception) {
+ if (MDatabaseConnection.TransactionStatus.SUCCESSFUL == this.getTransactionStatus()) {
+ this.setTransactionStatus(MDatabaseConnection.TransactionStatus.FAILED);
+ }
+ throw new MSqlStatementException(String.format("Could not execute prepared statement: %s.", preparedStatement), exception);
+ }
+ return results;
+ }
+
+ /* Tables. */
+
+ public MSqlTable getTable(String table, String primaryKey) {
+ return new MSqlTable(this, table, primaryKey);
+ }
+
+ /* Logging. */
+
+ public void setLogListener(MLogListener logListener) {
+ this.logListener = logListener;
+ }
+
+ protected MLogListener getLogListener() {
+ return this.logListener;
+ }
+
+ protected void logStatement(String statement) {
+ MLogListener logListener = this.getLogListener();
+ if (null != logListener) {
+ logListener.onMessageLogging(statement);
+ }
+ }
+
+ /* Engine version. */
+
+ public String getEngineVersion() throws MDatabaseConnectionFailureException, MSqlStatementException {
+ MSqlStatementResults results = this.executePreparedStatement("SELECT VERSION()");
+ LinkedList<LinkedHashMap<String, Object>> resultList = results.getRecords();
+ //
+ return (String)resultList.get(0).get("VERSION()");
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco.database;
+
+@SuppressWarnings("serial")
+public class MDatabaseConnectionFailureException extends MDatabaseException {
+
+ /* */
+
+ public MDatabaseConnectionFailureException() {
+ super();
+ }
+
+ public MDatabaseConnectionFailureException(String message) {
+ super(message);
+ }
+
+ public MDatabaseConnectionFailureException(Throwable error) {
+ super(error);
+ }
+
+ public MDatabaseConnectionFailureException(String message, Throwable error) {
+ super(message, error);
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco.database;
+
+import com.marcozanon.macaco.MObject;
+import com.marcozanon.macaco.logging.MLogListener;
+import com.marcozanon.macaco.text.MText;
+
+public class MDatabaseConnectionGenerator extends MObject {
+
+ protected String driver = null;
+ protected String url = null;
+ protected String username = null;
+ protected String password = null;
+ protected boolean localTypesMode = false;
+ protected MLogListener logListener = null;
+
+ /* */
+
+ public MDatabaseConnectionGenerator(String driver, String url, String username, String password, boolean localTypesMode, MLogListener logListener) {
+ super();
+ //
+ if (MText.isBlank(driver)) {
+ throw new IllegalArgumentException("Invalid 'driver': null or empty.");
+ }
+ if (MText.isBlank(url)) {
+ throw new IllegalArgumentException("Invalid 'url': null or empty.");
+ }
+ if (null == username) {
+ throw new IllegalArgumentException("Invalid 'username': null.");
+ }
+ if (null == password) {
+ throw new IllegalArgumentException("Invalid 'password': null.");
+ }
+ //
+ this.driver = driver;
+ this.url = url;
+ this.username = username;
+ this.password = password;
+ this.localTypesMode = localTypesMode;
+ this.setLogListener(logListener);
+ }
+
+ /* Driver. */
+
+ public String getDriver() {
+ return this.driver;
+ }
+
+ /* Url. */
+
+ public String getUrl() {
+ return this.url;
+ }
+
+ /* Username. */
+
+ public String getUsername() {
+ return this.username;
+ }
+
+ /* Password. */
+
+ public String getPassword() {
+ return this.password;
+ }
+
+ /* Local types mode. */
+
+ public boolean getLocalTypesMode() {
+ return this.localTypesMode;
+ }
+
+ /* Log listener. */
+
+ public MLogListener getLogListener() {
+ return this.logListener;
+ }
+
+ public void setLogListener(MLogListener logListener) {
+ this.logListener = logListener;
+ }
+
+ /* Generator. */
+
+ public MDatabaseConnection getNewDatabaseConnection() throws MDatabaseConnectionFailureException {
+ return new MDatabaseConnection(this.getDriver(), this.getUrl(), this.getUsername(), this.getPassword(), this.getLocalTypesMode(), this.getLogListener());
+ }
+
+ public boolean isGeneratorFor(MDatabaseConnection databaseConnection) {
+ if (null == databaseConnection) {
+ throw new IllegalArgumentException("Invalid 'databaseConnection': null.");
+ }
+ //
+ if (!databaseConnection.getDriver().equals(this.getDriver())) {
+ return false;
+ }
+ if (!databaseConnection.getUrl().equals(this.getUrl())) {
+ return false;
+ }
+ if (!databaseConnection.getUsername().equals(this.getUsername())) {
+ return false;
+ }
+ if (!databaseConnection.getPassword().equals(this.getPassword())) {
+ return false;
+ }
+ if (databaseConnection.getLocalTypesMode() != this.getLocalTypesMode()) {
+ return false;
+ }
+ if (databaseConnection.getLogListener() != this.getLogListener()) {
+ return false;
+ }
+ //
+ return true;
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco.database;
+
+import com.marcozanon.macaco.MObject;
+import com.marcozanon.macaco.logging.MLogListener;
+import java.util.LinkedList;
+
+public class MDatabaseConnectionPool extends MObject {
+
+ protected MDatabaseConnectionGenerator databaseConnectionGenerator = null;
+
+ protected LinkedList<MDatabaseConnection> databaseConnections = new LinkedList<MDatabaseConnection>();
+ protected int minimumSize = 0;
+ protected int maximumSize = 0;
+
+ /* */
+
+ public MDatabaseConnectionPool(String driver, String url, String username, String password, boolean localTypesMode, int minimumSize, int maximumSize, MLogListener logListener) throws MDatabaseConnectionFailureException {
+ super();
+ //
+ if (0 > minimumSize) {
+ throw new IllegalArgumentException(String.format("Invalid 'minimumSize': %s.", minimumSize));
+ }
+ if (1 > maximumSize) {
+ throw new IllegalArgumentException(String.format("Invalid 'maximumSize': %s.", maximumSize));
+ }
+ else if (minimumSize > maximumSize) {
+ throw new IllegalArgumentException(String.format("Invalid 'maximumSize': %s < %s ('minimumSize').", maximumSize, minimumSize));
+ }
+ //
+ this.databaseConnectionGenerator = new MDatabaseConnectionGenerator(driver, url, username, password, localTypesMode, logListener);
+ this.minimumSize = minimumSize;
+ this.maximumSize = maximumSize;
+ //
+ this.initialize();
+ }
+
+ protected void initialize() throws MDatabaseConnectionFailureException {
+ for (int c = 0; c < this.getMinimumSize(); c++) {
+ MDatabaseConnection databaseConnection = this.getDatabaseConnectionGenerator().getNewDatabaseConnection();
+ this.pushDatabaseConnection(databaseConnection);
+ }
+ }
+
+ protected void closeConnections() {
+ LinkedList<MDatabaseConnection> databaseConnections = this.getDatabaseConnections();
+ while (0 < databaseConnections.size()) {
+ MDatabaseConnection databaseConnection = databaseConnections.removeLast();
+ //
+ try {
+ databaseConnection.close();
+ }
+ catch (MDatabaseConnectionFailureException exception) {
+ }
+ }
+ }
+
+ @Override
+ protected void finalize() {
+ this.closeConnections();
+ }
+
+ /* Database connection generator. */
+
+ protected MDatabaseConnectionGenerator getDatabaseConnectionGenerator() {
+ return this.databaseConnectionGenerator;
+ }
+
+ /* Database connections. */
+
+ protected LinkedList<MDatabaseConnection> getDatabaseConnections() {
+ return this.databaseConnections;
+ }
+
+ protected int getMinimumSize() {
+ return this.minimumSize;
+ }
+
+ protected int getMaximumSize() {
+ return this.maximumSize;
+ }
+
+ public synchronized MDatabaseConnection popDatabaseConnection() throws MDatabaseConnectionFailureException {
+ LinkedList<MDatabaseConnection> databaseConnections = this.getDatabaseConnections();
+ MDatabaseConnection databaseConnection = null;
+ if (0 == databaseConnections.size()) {
+ databaseConnection = this.getDatabaseConnectionGenerator().getNewDatabaseConnection();
+ }
+ else {
+ databaseConnection = databaseConnections.removeLast();
+ }
+ if (this.getMinimumSize() > databaseConnections.size()) {
+ databaseConnections.add(this.getDatabaseConnectionGenerator().getNewDatabaseConnection());
+ }
+ return databaseConnection;
+ }
+
+ public synchronized void pushDatabaseConnection(MDatabaseConnection databaseConnection) throws MDatabaseConnectionFailureException {
+ if (null == databaseConnection) {
+ throw new IllegalArgumentException("Invalid 'databaseConnection': null.");
+ }
+ else if (databaseConnection.isClosed()) {
+ throw new IllegalArgumentException("Invalid 'databaseConnection': closed.");
+ }
+ //
+ if (!this.getDatabaseConnectionGenerator().isGeneratorFor(databaseConnection)) {
+ databaseConnection.close();
+ }
+ else {
+ LinkedList<MDatabaseConnection> databaseConnections = this.getDatabaseConnections();
+ if (this.getMaximumSize() >= databaseConnections.size()) {
+ databaseConnections.add(databaseConnection);
+ }
+ }
+ }
+
+ /* Logging. */
+
+ public synchronized void setLogListener(MLogListener logListener) {
+ this.getDatabaseConnectionGenerator().setLogListener(logListener);
+ //
+ for (MDatabaseConnection databaseConnection: this.getDatabaseConnections()) {
+ databaseConnection.setLogListener(logListener);
+ }
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco.database;
+
+import com.marcozanon.macaco.MException;
+
+public abstract class MDatabaseException extends MException {
+
+ /* */
+
+ public MDatabaseException() {
+ super();
+ }
+
+ public MDatabaseException(String message) {
+ super(message);
+ }
+
+ public MDatabaseException(Throwable error) {
+ super(error);
+ }
+
+ public MDatabaseException(String message, Throwable error) {
+ super(message, error);
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco.database;
+
+@SuppressWarnings("serial")
+public class MSqlStatementException extends MDatabaseException {
+
+ /* */
+
+ public MSqlStatementException() {
+ super();
+ }
+
+ public MSqlStatementException(String message) {
+ super(message);
+ }
+
+ public MSqlStatementException(Throwable error) {
+ super(error);
+ }
+
+ public MSqlStatementException(String message, Throwable error) {
+ super(message, error);
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco.database;
+
+import com.marcozanon.macaco.MObject;
+import com.marcozanon.macaco.text.MText;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+
+public class MSqlStatementResults extends MObject {
+
+ protected PreparedStatement preparedStatement = null;
+ protected boolean localTypesMode = false;
+
+ protected ResultSet resultSet = null;
+
+ protected LinkedList<LinkedHashMap<String, Object>> records = new LinkedList<LinkedHashMap<String, Object>>();
+
+ protected LinkedHashSet<Object> generatedKeys = new LinkedHashSet<Object>();
+
+ /* */
+
+ public MSqlStatementResults(PreparedStatement preparedStatement, boolean localTypesMode) throws MSqlStatementException {
+ super();
+ //
+ if (null == preparedStatement) {
+ throw new IllegalArgumentException("Invalid 'preparedStatement': null.");
+ }
+ //
+ this.preparedStatement = preparedStatement;
+ this.localTypesMode = localTypesMode;
+ //
+ try {
+ this.resultSet = this.getPreparedStatement().getResultSet();
+ if (null != this.getResultSet()) {
+ while (this.getResultSet().next()) {
+ ResultSetMetaData resultSetMetaData = this.getResultSet().getMetaData();
+ LinkedHashMap<String, Object> record = new LinkedHashMap<String, Object>();
+ //
+ for (int f = 0; resultSetMetaData.getColumnCount() > f; f++) {
+ String field = resultSetMetaData.getColumnLabel(f + 1);
+ Object value = this.getResultSet().getObject(field);
+ //
+ if ((value instanceof java.sql.Date) && (this.getLocalTypesMode())) {
+ value = ((java.sql.Date)value).toLocalDate();
+ }
+ else if ((value instanceof java.sql.Time) && (this.getLocalTypesMode())) {
+ value = ((java.sql.Time)value).toLocalTime();
+ }
+ else if ((value instanceof java.sql.Timestamp) && (this.getLocalTypesMode())) {
+ value = ((java.sql.Timestamp)value).toLocalDateTime();
+ }
+ //
+ record.put(field, value);
+ }
+ //
+ this.getRecords().add(record);
+ }
+ }
+ }
+ catch (SQLException exception) {
+ throw new MSqlStatementException("Could not retrieve statement records.", exception);
+ }
+ //
+ try {
+ ResultSet generatedKeysResultSet = this.getPreparedStatement().getGeneratedKeys();
+ while (generatedKeysResultSet.next()) {
+ this.getGeneratedKeys().add(generatedKeysResultSet.getObject(1));
+ }
+ generatedKeysResultSet.close();
+ }
+ catch (SQLException exception) {
+ throw new MSqlStatementException("Could not retrieve generated keys.", exception);
+ }
+ }
+
+ public void close() throws MDatabaseConnectionFailureException {
+ try {
+ if (null != this.getResultSet()) {
+ this.getResultSet().close();
+ }
+ this.getPreparedStatement().close();
+ }
+ catch (SQLException exception) {
+ throw new MDatabaseConnectionFailureException("Could not close statement and/or result set references.", exception);
+ }
+ }
+
+ @Override
+ protected void finalize() {
+ try {
+ this.close();
+ }
+ catch (Exception exception) {
+ }
+ }
+
+ /* References. */
+
+ protected PreparedStatement getPreparedStatement() {
+ return this.preparedStatement;
+ }
+
+ public ResultSet getResultSet() {
+ return this.resultSet;
+ }
+
+ public boolean getLocalTypesMode() {
+ return this.localTypesMode;
+ }
+
+ /* Records. */
+
+ public LinkedList<LinkedHashMap<String, Object>> getRecords() {
+ return this.records;
+ }
+
+ public LinkedList<Object> getRecordsByField(String field) {
+ if (MText.isBlank(field)) {
+ throw new IllegalArgumentException("Invalid 'field': null or empty.");
+ }
+ //
+ LinkedList<Object> recordsByField = new LinkedList<Object>();
+ for (LinkedHashMap<String, Object> record: this.getRecords()) {
+ Object r = record.get(field);
+ if (null == r) {
+ throw new IllegalArgumentException(String.format("Invalid 'field': %s.", field));
+ }
+ recordsByField.add(r);
+ }
+ return recordsByField;
+ }
+
+ /* Generated keys. */
+
+ public LinkedHashSet<Object> getGeneratedKeys() {
+ return this.generatedKeys;
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco.database;
+
+import com.marcozanon.macaco.MObject;
+import com.marcozanon.macaco.text.MText;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.StringJoiner;
+
+public class MSqlTable extends MObject {
+
+ protected MDatabaseConnection databaseConnection = null;
+ protected String table = null;
+ protected String primaryKey = null;
+
+ /* */
+
+ public MSqlTable(MDatabaseConnection databaseConnection, String table, String primaryKey) {
+ super();
+ //
+ if (null == databaseConnection) {
+ throw new IllegalArgumentException("Invalid 'databaseConnection': null.");
+ }
+ if (MText.isBlank(table)) {
+ throw new IllegalArgumentException("Invalid 'table': null or empty.");
+ }
+ if (MText.isBlank(primaryKey)) {
+ throw new IllegalArgumentException("Invalid 'primaryKey': null or empty.");
+ }
+ //
+ this.databaseConnection = databaseConnection;
+ this.table = table;
+ this.primaryKey = primaryKey;
+ }
+
+ /* Database connection. */
+
+ protected MDatabaseConnection getDatabaseConnection() {
+ return this.databaseConnection;
+ }
+
+ /* Table. */
+
+ public String getTable() {
+ return this.table;
+ }
+
+ /* Primary key. */
+
+ public String getPrimaryKey() {
+ return this.primaryKey;
+ }
+
+ /* Statements. */
+
+ protected MSqlStatementResults insertRecord(LinkedHashMap<String, Object> map, boolean loggableStatement) throws MDatabaseConnectionFailureException, MSqlStatementException {
+ if (null == map) {
+ throw new IllegalArgumentException("Invalid 'map': null.");
+ }
+ //
+ LinkedList<Object> p = new LinkedList<Object>();
+ StringBuilder fields = new StringBuilder("");
+ StringBuilder s = new StringBuilder("");
+ for (String field: map.keySet()) {
+ fields.append("\"" + this.getTable() + "\".\"" + field + "\", ");
+ s.append("?, ");
+ p.add(map.get(field));
+ }
+ fields.delete(fields.length() - 2, fields.length());
+ s.delete(s.length() - 2, s.length());
+ //
+ return this.getDatabaseConnection().executePreparedStatement(" INSERT INTO \"" + this.getTable() + "\""
+ + " (" + fields + ")"
+ + " VALUES (" + s + ")",
+ p, true, loggableStatement);
+ }
+
+ protected MSqlStatementResults updateRecord(LinkedHashMap<String, Object> map, Object id, boolean loggableStatement) throws MDatabaseConnectionFailureException, MSqlStatementException {
+ if (null == map) {
+ throw new IllegalArgumentException("Invalid 'map': null.");
+ }
+ if (null == id) {
+ throw new IllegalArgumentException("Invalid 'id': null.");
+ }
+ //
+ LinkedList<Object> p = new LinkedList<Object>();
+ StringBuilder s = new StringBuilder("");
+ for (String field: map.keySet()) {
+ s.append("\"" + this.getTable() + "\".\"" + field + "\" = ?, ");
+ p.add(map.get(field));
+ }
+ s.delete(s.length() - 2, s.length());
+ p.add(id);
+ //
+ return this.getDatabaseConnection().executePreparedStatement(" UPDATE \"" + this.getTable() + "\""
+ + " SET " + s
+ + " WHERE (\"" + this.getTable() + "\".\"" + this.getPrimaryKey() + "\" = ?)",
+ p, true, loggableStatement);
+ }
+
+ public MSqlStatementResults setRecord(LinkedHashMap<String, Object> map, Object id) throws MDatabaseConnectionFailureException, MSqlStatementException {
+ return this.setRecord(map, id, true);
+ }
+
+ public MSqlStatementResults setRecord(LinkedHashMap<String, Object> map, Object id, boolean loggableStatement) throws MDatabaseConnectionFailureException, MSqlStatementException {
+ if (null == id) {
+ return this.insertRecord(map, loggableStatement);
+ }
+ else {
+ return this.updateRecord(map, id, loggableStatement);
+ }
+ }
+
+ public MSqlStatementResults getRecord(Object id) throws MDatabaseConnectionFailureException, MSqlStatementException {
+ if (null == id) {
+ throw new IllegalArgumentException("Invalid 'id': null.");
+ }
+ //
+ LinkedList<Object> p = new LinkedList<Object>();
+ p.add(id);
+ //
+ return this.getDatabaseConnection().executePreparedStatement(" SELECT *"
+ + " FROM \"" + this.getTable() + "\""
+ + " WHERE (\"" + this.getTable() + "\".\"" + this.getPrimaryKey() + "\" = ?)",
+ p);
+ }
+
+ public MSqlStatementResults deleteRecord(Object id) throws MDatabaseConnectionFailureException, MSqlStatementException {
+ if (null == id) {
+ throw new IllegalArgumentException("Invalid 'id': null.");
+ }
+ //
+ LinkedList<Object> p = new LinkedList<Object>();
+ p.add(id);
+ //
+ return this.getDatabaseConnection().executePreparedStatement(" DELETE FROM \"" + this.getTable() + "\""
+ + " WHERE (\"" + this.getTable() + "\".\"" + this.getPrimaryKey() + "\" = ?)",
+ p);
+ }
+
+ public MSqlStatementResults deleteRecords(LinkedHashSet idSet) throws MDatabaseConnectionFailureException, MSqlStatementException {
+ if (null == idSet) {
+ throw new IllegalArgumentException("Invalid 'idSet': null.");
+ }
+ //
+ String whereClause = null;
+ LinkedList<Object> p = null;
+ if (0 == idSet.size()) {
+ whereClause = "0";
+ }
+ else {
+ StringJoiner s = new StringJoiner(", ");
+ p = new LinkedList<Object>();
+ for (Object id: idSet) {
+ s.add("?");
+ p.add(id);
+ }
+ whereClause = "\"" + this.getTable() + "\".\"" + this.getPrimaryKey() + "\" IN (" + s.toString() + ")";
+ }
+ //
+ return this.getDatabaseConnection().executePreparedStatement(" DELETE FROM \"" + this.getTable() + "\""
+ + " WHERE (" + whereClause + ")",
+ p);
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco.database;
+
+@SuppressWarnings("serial")
+public class MSqlTransactionException extends MDatabaseException {
+
+ /* */
+
+ public MSqlTransactionException() {
+ super();
+ }
+
+ public MSqlTransactionException(String message) {
+ super(message);
+ }
+
+ public MSqlTransactionException(Throwable error) {
+ super(error);
+ }
+
+ public MSqlTransactionException(String message, Throwable error) {
+ super(message, error);
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco.json;
+
+@SuppressWarnings("serial")
+public class MInvalidJsonValueException extends MJsonException {
+
+ /* */
+
+ public MInvalidJsonValueException() {
+ super();
+ }
+
+ public MInvalidJsonValueException(String message) {
+ super(message);
+ }
+
+ public MInvalidJsonValueException(Throwable error) {
+ super(error);
+ }
+
+ public MInvalidJsonValueException(String message, Throwable error) {
+ super(message, error);
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco.json;
+
+import com.marcozanon.macaco.text.MText;
+import java.util.LinkedList;
+
+public class MJsonArray extends MJsonValue {
+
+ protected static enum ParsingMode {
+ START,
+ POST_VALUE,
+ POST_VALUE_SEPARATOR,
+ ERROR
+ };
+
+ protected LinkedList<MJsonValue> values = new LinkedList<MJsonValue>();
+
+ /* */
+
+ public MJsonArray() throws MInvalidJsonValueException {
+ this("[]");
+ }
+
+ public MJsonArray(String x) throws MInvalidJsonValueException {
+ super();
+ //
+ this.parseString(x);
+ }
+
+ @Override
+ public MJsonArray clone() {
+ MJsonArray tmpMJsonArray = null;
+ try {
+ tmpMJsonArray = new MJsonArray(this.getJsonValue());
+ }
+ catch (MInvalidJsonValueException exception) { // cannot happen
+ }
+ return tmpMJsonArray;
+ }
+
+ /* Value handlers. */
+
+ public void addValue(MJsonValue x) {
+ if (null == x) {
+ throw new IllegalArgumentException("Invalid 'x': null.");
+ }
+ //
+ this.getValues().add(x);
+ }
+
+ public void setValue(int index, MJsonValue x) {
+ if ((0 > index) || ((this.getValueCount() - 1) < index)) {
+ throw new IllegalArgumentException(String.format("Invalid 'index': %s: out of range.", index));
+ }
+ if (null == x) {
+ throw new IllegalArgumentException("Invalid 'x': null.");
+ }
+ //
+ this.getValues().set(index, x);
+ }
+
+ protected LinkedList<MJsonValue> getValues() {
+ return this.values;
+ }
+
+ public MJsonValue getValue(int index) {
+ if ((0 > index) || ((this.getValueCount() - 1) < index)) {
+ throw new IllegalArgumentException(String.format("Invalid 'index': %s: out of range.", index));
+ }
+ //
+ return this.getValues().get(index).clone();
+ }
+
+ public int getValueCount() {
+ return this.getValues().size();
+ }
+
+ public void removeValue(int index) {
+ if ((0 > index) || ((this.getValueCount() - 1) < index)) {
+ throw new IllegalArgumentException(String.format("Invalid 'index': %s: out of range.", index));
+ }
+ //
+ this.getValues().remove(index);
+ }
+
+ public void clearValues() {
+ this.getValues().clear();
+ }
+
+ /* Parsers. */
+
+ protected static int getTokenLength(String x) {
+ if (null == x) {
+ throw new IllegalArgumentException("Invalid 'x': null.");
+ }
+ //
+ if ((2 > x.length()) || ('[' != x.charAt(0))) {
+ return 0;
+ }
+ int position = x.indexOf("]", 1);
+ while (-1 < position) {
+ try {
+ MJsonArray testValue = new MJsonArray();
+ testValue.parseString(x.substring(0, position + 1));
+ return position + 1;
+ }
+ catch (MInvalidJsonValueException exception) {
+ position = x.indexOf("]", position + 1);
+ }
+ }
+ return 0;
+ }
+
+ public void parseString(String x) throws MInvalidJsonValueException {
+ if (MText.isBlank(x)) {
+ throw new IllegalArgumentException("Invalid 'x': null or empty.");
+ }
+ //
+ if ((2 > x.length()) || ('[' != x.charAt(0)) || (']' != x.charAt(x.length() - 1))) {
+ throw new MInvalidJsonValueException(String.format("Invalid 'x': %s: must begin and end with square brackets.", x));
+ }
+ String y = x.substring(1, x.length() - 1);
+ int parsingPosition = 0;
+ MJsonArray.ParsingMode parsingMode = MJsonArray.ParsingMode.START;
+ while ((y.length() > parsingPosition) && (MJsonArray.ParsingMode.ERROR != parsingMode)) {
+ int c = y.codePointAt(parsingPosition);
+ switch (parsingMode) {
+ case START:
+ case POST_VALUE_SEPARATOR: // a value is expected
+ if (MJsonValue.isWhitespace(c)) {
+ parsingPosition++;
+ }
+ else if ((MJsonValue.isValueSeparator(c)) || (MJsonValue.isNameSeparator(c)) || (MJsonValue.isClosingStructuralCharacter(c))) {
+ parsingMode = MJsonArray.ParsingMode.ERROR;
+ }
+ else {
+ int offset = 0;
+ if (0 < (offset = MJsonNull.getTokenLength(y.substring(parsingPosition)))) {
+ this.addValue(new MJsonNull(y.substring(parsingPosition, parsingPosition + offset)));
+ }
+ else if (0 < (offset = MJsonBoolean.getTokenLength(y.substring(parsingPosition)))) {
+ this.addValue(new MJsonBoolean(y.substring(parsingPosition, parsingPosition + offset)));
+ }
+ else if (0 < (offset = MJsonNumber.getTokenLength(y.substring(parsingPosition)))) {
+ this.addValue(new MJsonNumber(y.substring(parsingPosition, parsingPosition + offset)));
+ }
+ else if (0 < (offset = MJsonString.getTokenLength(y.substring(parsingPosition)))) {
+ this.addValue(new MJsonString(y.substring(parsingPosition, parsingPosition + offset)));
+ }
+ else if (0 < (offset = MJsonArray.getTokenLength(y.substring(parsingPosition)))) {
+ this.addValue(new MJsonArray(y.substring(parsingPosition, parsingPosition + offset)));
+ }
+ else if (0 < (offset = MJsonObject.getTokenLength(y.substring(parsingPosition)))) {
+ this.addValue(new MJsonObject(y.substring(parsingPosition, parsingPosition + offset)));
+ }
+ if (0 < offset) {
+ parsingPosition += offset;
+ parsingMode = MJsonArray.ParsingMode.POST_VALUE;
+ }
+ else {
+ parsingMode = MJsonArray.ParsingMode.ERROR;
+ }
+ }
+ break;
+ case POST_VALUE:
+ if (MJsonValue.isWhitespace(c)) {
+ parsingPosition++;
+ }
+ else if (MJsonValue.isValueSeparator(c)) {
+ parsingPosition++;
+ parsingMode = MJsonArray.ParsingMode.POST_VALUE_SEPARATOR;
+ }
+ else {
+ parsingMode = MJsonArray.ParsingMode.ERROR;
+ }
+ break;
+ case ERROR: // malformed string
+ break;
+ }
+ }
+ if (MJsonArray.ParsingMode.POST_VALUE_SEPARATOR == parsingMode) {
+ parsingMode = MJsonArray.ParsingMode.ERROR;
+ }
+ if (MJsonArray.ParsingMode.ERROR == parsingMode) {
+ throw new MInvalidJsonValueException(String.format("Invalid 'x': %s: not a Json array.", x));
+ }
+ }
+
+ /* Formatter. */
+
+ @Override
+ public String getJsonValue() {
+ StringBuilder s = new StringBuilder("");
+ s.append("[");
+ for (int i = 0; this.getValueCount() > i; i++) {
+ if (0 < i) {
+ s.append(", ");
+ }
+ s.append(this.getValues().get(i).getJsonValue());
+ }
+ s.append("]");
+ return s.toString();
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco.json;
+
+import com.marcozanon.macaco.text.MText;
+
+public class MJsonBoolean extends MJsonValue {
+
+ protected Boolean value = null;
+
+ /* */
+
+ public MJsonBoolean() throws MInvalidJsonValueException {
+ this("false");
+ }
+
+ public MJsonBoolean(String x) throws MInvalidJsonValueException {
+ super();
+ //
+ this.parseString(x);
+ }
+
+ @Override
+ public MJsonBoolean clone() {
+ MJsonBoolean tmpMJsonBoolean = null;
+ try {
+ tmpMJsonBoolean = new MJsonBoolean(this.getJsonValue());
+ }
+ catch (MInvalidJsonValueException exception) { // cannot happen
+ }
+ return tmpMJsonBoolean;
+ }
+
+ /* Value handlers. */
+
+ public void setValue(Boolean x) {
+ if (null == x) {
+ throw new IllegalArgumentException("Invalid 'x': null.");
+ }
+ //
+ this.value = x;
+ }
+
+ public Boolean getValue() {
+ return this.value;
+ }
+
+ /* Parsers. */
+
+ protected static int getTokenLength(String x) {
+ if (null == x) {
+ throw new IllegalArgumentException("Invalid 'x': null.");
+ }
+ //
+ if (x.startsWith("true")) {
+ return "true".length();
+ }
+ else if (x.startsWith("false")) {
+ return "false".length();
+ }
+ return 0;
+ }
+
+ public void parseString(String x) throws MInvalidJsonValueException {
+ if (MText.isBlank(x)) {
+ throw new IllegalArgumentException("Invalid 'x': null or empty.");
+ }
+ //
+ if ((!"true".equals(x)) && (!"false".equals(x))) {
+ throw new MInvalidJsonValueException(String.format("Invalid 'x': %s: not a Json boolean.", x));
+ }
+ this.setValue(Boolean.valueOf(x));
+ }
+
+ /* Formatter. */
+
+ @Override
+ public String getJsonValue() {
+ return this.getValue().toString();
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco.json;
+
+import com.marcozanon.macaco.MException;
+
+public abstract class MJsonException extends MException {
+
+ /* */
+
+ public MJsonException() {
+ super();
+ }
+
+ public MJsonException(String message) {
+ super(message);
+ }
+
+ public MJsonException(Throwable error) {
+ super(error);
+ }
+
+ public MJsonException(String message, Throwable error) {
+ super(message, error);
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco.json;
+
+import com.marcozanon.macaco.text.MText;
+
+public class MJsonNull extends MJsonValue {
+
+ /* */
+
+ public MJsonNull() throws MInvalidJsonValueException {
+ this("null");
+ }
+
+ public MJsonNull(String x) throws MInvalidJsonValueException {
+ super();
+ //
+ this.parseString(x);
+ }
+
+ @Override
+ public MJsonNull clone() {
+ MJsonNull tmpMJsonNull = null;
+ try {
+ tmpMJsonNull = new MJsonNull(this.getJsonValue());
+ }
+ catch (MInvalidJsonValueException exception) { // cannot happen
+ }
+ return tmpMJsonNull;
+ }
+
+ /* Parsers. */
+
+ protected static int getTokenLength(String x) {
+ if (null == x) {
+ throw new IllegalArgumentException("Invalid 'x': null.");
+ }
+ //
+ if (x.startsWith("null")) {
+ return "null".length();
+ }
+ return 0;
+ }
+
+ public void parseString(String x) throws MInvalidJsonValueException {
+ if (MText.isBlank(x)) {
+ throw new IllegalArgumentException("Invalid 'x': null or empty.");
+ }
+ //
+ if (!"null".equals(x)) {
+ throw new MInvalidJsonValueException(String.format("Invalid 'x': %s: not a Json null.", x));
+ }
+ }
+
+ /* Formatter. */
+
+ @Override
+ public String getJsonValue() {
+ return "null";
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco.json;
+
+import com.marcozanon.macaco.text.MText;
+import java.math.BigDecimal;
+
+public class MJsonNumber extends MJsonValue {
+
+ protected BigDecimal value = null;
+
+ /* */
+
+ public MJsonNumber() throws MInvalidJsonValueException {
+ this("0");
+ }
+
+ public MJsonNumber(String x) throws MInvalidJsonValueException {
+ super();
+ //
+ this.parseString(x);
+ }
+
+ @Override
+ public MJsonNumber clone() {
+ MJsonNumber tmpMJsonNumber = null;
+ try {
+ tmpMJsonNumber = new MJsonNumber(this.getJsonValue());
+ }
+ catch (MInvalidJsonValueException exception) { // cannot happen
+ }
+ return tmpMJsonNumber;
+ }
+
+ /* Value handlers. */
+
+ public void setValue(Number x) {
+ if (null == x) {
+ throw new IllegalArgumentException("Invalid 'x': null.");
+ }
+ //
+ this.value = new BigDecimal(x.toString());
+ }
+
+ public BigDecimal getValue() {
+ return this.value;
+ }
+
+ /* Parsers. */
+
+ protected static int getTokenLength(String x) {
+ if (null == x) {
+ throw new IllegalArgumentException("Invalid 'x': null.");
+ }
+ //
+ if (' ' == x.charAt(0)) {
+ return 0;
+ }
+ int position = 0;
+ int validPosition = 0;
+ BigDecimal y = null;
+ while (x.length() > position) {
+ if (' ' == x.charAt(position)) {
+ break;
+ }
+ else {
+ try {
+ y = new BigDecimal(x.substring(0, position + 1));
+ position++;
+ validPosition = position;
+ }
+ catch (NumberFormatException exception) {
+ position++;
+ }
+ }
+ }
+ return validPosition;
+ }
+
+ public void parseString(String x) throws MInvalidJsonValueException {
+ if (MText.isBlank(x)) {
+ throw new IllegalArgumentException("Invalid 'x': null or empty.");
+ }
+ //
+ BigDecimal y = null;
+ try {
+ y = new BigDecimal(x);
+ }
+ catch (NumberFormatException exception) {
+ throw new MInvalidJsonValueException(String.format("Invalid 'x': %s: not a Json number.", x)); // no need to propagate exception
+ }
+ this.setValue(y);
+ }
+
+ /* Formatter. */
+
+ @Override
+ public String getJsonValue() {
+ return this.getValue().toString();
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco.json;
+
+import com.marcozanon.macaco.text.MText;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+
+public class MJsonObject extends MJsonValue {
+
+ protected static enum ParsingMode {
+ START,
+ POST_NAME,
+ POST_NAME_SEPARATOR,
+ POST_VALUE,
+ POST_VALUE_SEPARATOR,
+ ERROR
+ };
+
+ protected LinkedHashMap<String, MJsonValue> values = new LinkedHashMap<String, MJsonValue>();
+
+ /* */
+
+ public MJsonObject() throws MInvalidJsonValueException {
+ this("{}");
+ }
+
+ public MJsonObject(String x) throws MInvalidJsonValueException {
+ super();
+ //
+ this.parseString(x);
+ }
+
+ @Override
+ public MJsonObject clone() {
+ MJsonObject tmpMJsonObject = null;
+ try {
+ tmpMJsonObject = new MJsonObject(this.getJsonValue());
+ }
+ catch (MInvalidJsonValueException exception) { // cannot happen
+ }
+ return tmpMJsonObject;
+ }
+
+ /* Value handlers. */
+
+ public void setValue(String key, MJsonValue x) {
+ if (MText.isBlank(key)) {
+ throw new IllegalArgumentException("Invalid 'key': null or empty.");
+ }
+ if (null == x) {
+ throw new IllegalArgumentException("Invalid 'x': null.");
+ }
+ //
+ this.getValues().put(key, x);
+ }
+
+ protected LinkedHashMap<String, MJsonValue> getValues() {
+ return this.values;
+ }
+
+ public MJsonValue getValue(String key) {
+ if (MText.isBlank(key)) {
+ throw new IllegalArgumentException("Invalid 'key': null or empty.");
+ }
+ //
+ if (!this.containsKey(key)) {
+ throw new IllegalArgumentException(String.format("Invalid 'key': %s: not available.", key));
+ }
+ return this.getValues().get(key).clone();
+ }
+
+ public int getValueCount() {
+ return this.getValues().size();
+ }
+
+ public void removeValue(String key) {
+ if (MText.isBlank(key)) {
+ throw new IllegalArgumentException("Invalid 'key': null or empty.");
+ }
+ //
+ if (!this.containsKey(key)) {
+ throw new IllegalArgumentException(String.format("Invalid 'key': %s: not available.", key));
+ }
+ this.getValues().remove(key);
+ }
+
+ public void clearValues() {
+ this.getValues().clear();
+ }
+
+ public boolean containsKey(String key) {
+ if (MText.isBlank(key)) {
+ throw new IllegalArgumentException("Invalid 'key': null or empty.");
+ }
+ //
+ return this.getValues().containsKey(key);
+ }
+
+ public LinkedHashSet<String> getKeys() {
+ LinkedHashSet<String> keys = new LinkedHashSet<String>();
+ for (String key: this.getValues().keySet()) {
+ keys.add(key);
+ }
+ return keys;
+ }
+
+ /* Parsers. */
+
+ protected static int getTokenLength(String x) {
+ if (null == x) {
+ throw new IllegalArgumentException("Invalid 'x': null.");
+ }
+ //
+ if ((2 > x.length()) || ('{' != x.charAt(0))) {
+ return 0;
+ }
+ int position = x.indexOf("}", 1);
+ while (-1 < position) {
+ try {
+ MJsonObject testValue = new MJsonObject();
+ testValue.parseString(x.substring(0, position + 1));
+ return position + 1;
+ }
+ catch (MInvalidJsonValueException exception) {
+ position = x.indexOf("}", position + 1);
+ }
+ }
+ return 0;
+ }
+
+ public void parseString(String x) throws MInvalidJsonValueException {
+ if (MText.isBlank(x)) {
+ throw new IllegalArgumentException("Invalid 'x': null or empty.");
+ }
+ //
+ if ((2 > x.length()) || ('{' != x.charAt(0)) || ('}' != x.charAt(x.length() - 1))) {
+ throw new MInvalidJsonValueException(String.format("Invalid 'x': %s: must begin and end with curly brackets.", x));
+ }
+ String y = x.substring(1, x.length() - 1);
+ int parsingPosition = 0;
+ MJsonObject.ParsingMode parsingMode = MJsonObject.ParsingMode.START;
+ String lastKey = "";
+ while ((y.length() > parsingPosition) && (MJsonObject.ParsingMode.ERROR != parsingMode)) {
+ int c = y.codePointAt(parsingPosition);
+ switch (parsingMode) {
+ case START:
+ case POST_VALUE_SEPARATOR: // a name is expected
+ if (MJsonValue.isWhitespace(c)) {
+ parsingPosition++;
+ }
+ else if ((MJsonValue.isValueSeparator(c)) || (MJsonValue.isNameSeparator(c)) || (MJsonValue.isOpeningStructuralCharacter(c)) || (MJsonValue.isClosingStructuralCharacter(c))) {
+ parsingMode = MJsonObject.ParsingMode.ERROR;
+ }
+ else {
+ int offset = MJsonString.getTokenLength(y.substring(parsingPosition));
+ if (0 < offset) {
+ lastKey = (new MJsonString(y.substring(parsingPosition, parsingPosition + offset))).getValue();
+ parsingPosition += offset;
+ parsingMode = MJsonObject.ParsingMode.POST_NAME;
+ }
+ else {
+ parsingMode = MJsonObject.ParsingMode.ERROR;
+ }
+ }
+ break;
+ case POST_NAME:
+ if (MJsonValue.isWhitespace(c)) {
+ parsingPosition++;
+ }
+ else if (MJsonValue.isNameSeparator(c)) {
+ parsingPosition++;
+ parsingMode = MJsonObject.ParsingMode.POST_NAME_SEPARATOR;
+ }
+ else {
+ parsingMode = MJsonObject.ParsingMode.ERROR;
+ }
+ break;
+ case POST_NAME_SEPARATOR: // a value is expected
+ if (MJsonValue.isWhitespace(c)) {
+ parsingPosition++;
+ }
+ else if ((MJsonValue.isValueSeparator(c)) || (MJsonValue.isNameSeparator(c)) || (MJsonValue.isClosingStructuralCharacter(c))) {
+ parsingMode = MJsonObject.ParsingMode.ERROR;
+ }
+ else {
+ int offset = 0;
+ if (0 < (offset = MJsonNull.getTokenLength(y.substring(parsingPosition)))) {
+ this.setValue(lastKey, new MJsonNull(y.substring(parsingPosition, parsingPosition + offset)));
+ }
+ else if (0 < (offset = MJsonBoolean.getTokenLength(y.substring(parsingPosition)))) {
+ this.setValue(lastKey, new MJsonBoolean(y.substring(parsingPosition, parsingPosition + offset)));
+ }
+ else if (0 < (offset = MJsonNumber.getTokenLength(y.substring(parsingPosition)))) {
+ this.setValue(lastKey, new MJsonNumber(y.substring(parsingPosition, parsingPosition + offset)));
+ }
+ else if (0 < (offset = MJsonString.getTokenLength(y.substring(parsingPosition)))) {
+ this.setValue(lastKey, new MJsonString(y.substring(parsingPosition, parsingPosition + offset)));
+ }
+ else if (0 < (offset = MJsonArray.getTokenLength(y.substring(parsingPosition)))) {
+ this.setValue(lastKey, new MJsonArray(y.substring(parsingPosition, parsingPosition + offset)));
+ }
+ else if (0 < (offset = MJsonObject.getTokenLength(y.substring(parsingPosition)))) {
+ this.setValue(lastKey, new MJsonObject(y.substring(parsingPosition, parsingPosition + offset)));
+ }
+ if (0 < offset) {
+ lastKey = "";
+ parsingPosition += offset;
+ parsingMode = MJsonObject.ParsingMode.POST_VALUE;
+ }
+ else {
+ parsingMode = MJsonObject.ParsingMode.ERROR;
+ }
+ }
+ break;
+ case POST_VALUE:
+ if (MJsonValue.isWhitespace(c)) {
+ parsingPosition++;
+ }
+ else if (MJsonValue.isValueSeparator(c)) {
+ parsingPosition++;
+ parsingMode = MJsonObject.ParsingMode.POST_VALUE_SEPARATOR;
+ }
+ else {
+ parsingMode = MJsonObject.ParsingMode.ERROR;
+ }
+ break;
+ case ERROR: // malformed string
+ break;
+ }
+ }
+ if ((MJsonObject.ParsingMode.POST_NAME == parsingMode) || (MJsonObject.ParsingMode.POST_NAME_SEPARATOR == parsingMode) || (MJsonObject.ParsingMode.POST_VALUE_SEPARATOR == parsingMode)) {
+ parsingMode = MJsonObject.ParsingMode.ERROR;
+ }
+ if (MJsonObject.ParsingMode.ERROR == parsingMode) {
+ throw new MInvalidJsonValueException(String.format("Invalid 'x': %s: not a Json object.", x));
+ }
+ }
+
+ /* Formatter. */
+
+ @Override
+ public String getJsonValue() {
+ StringBuilder s = new StringBuilder("");
+ s.append("{");
+ int k = 0;
+ for (String key: this.getValues().keySet()) {
+ if (0 < k) {
+ s.append(", ");
+ }
+ s.append("\"" + MJsonString.getEscapedString(key, false) + "\"");
+ s.append(": ");
+ s.append(this.getValues().get(key).getJsonValue());
+ k++;
+ }
+ s.append("}");
+ return s.toString();
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco.json;
+
+import com.marcozanon.macaco.text.MText;
+
+public class MJsonString extends MJsonValue {
+
+ protected boolean extendedEscapeMode = false;
+
+ protected String value = null;
+
+ /* */
+
+ public MJsonString() throws MInvalidJsonValueException {
+ this("\"\"");
+ }
+
+ public MJsonString(String x) throws MInvalidJsonValueException {
+ this(x, false);
+ }
+
+ public MJsonString(String x, boolean extendedEscapeMode) throws MInvalidJsonValueException {
+ super();
+ //
+ this.setExtendedEscapeMode(extendedEscapeMode);
+ this.parseString(x);
+ }
+
+ @Override
+ public MJsonString clone() {
+ MJsonString tmpMJsonString = null;
+ try {
+ tmpMJsonString = new MJsonString(this.getJsonValue());
+ }
+ catch (MInvalidJsonValueException exception) { // cannot happen
+ }
+ return tmpMJsonString;
+ }
+
+ /* Extended escape mode. */
+
+ public void setExtendedEscapeMode(boolean extendedEscapeMode) {
+ this.extendedEscapeMode = extendedEscapeMode;
+ }
+
+ public boolean getExtendedEscapeMode() {
+ return this.extendedEscapeMode;
+ }
+
+ /* Value handlers. */
+
+ public void setValue(String x) {
+ if (null == x) {
+ throw new IllegalArgumentException("Invalid 'x': null.");
+ }
+ //
+ this.value = x;
+ }
+
+ public String getValue() {
+ return this.value;
+ }
+
+ /* Helpers. */
+
+ protected static String getEscapedString(String x, boolean extendedEscapeMode) {
+ if (null == x) {
+ throw new IllegalArgumentException("Invalid 'x': null.");
+ }
+ //
+ StringBuilder y = new StringBuilder();
+ for (int i = 0; x.length() > i; i++) {
+ int c = x.codePointAt(i);
+ if (extendedEscapeMode) {
+ if (0x00001F >= c) { // control Ascii character (0x000000-0x00001F)
+ y.append("\\u" + String.format("%4H", c).replace(" ", "0"));
+ }
+ else if (0x00007F >= c) { // non-control Ascii character (0x000020-0x00007F)
+ if ('"' == c) {
+ y.append("\\\"");
+ }
+ else if ('\\' == c) {
+ y.append("\\\\");
+ }
+ else if ('/' == c) {
+ y.append("\\/");
+ }
+ else {
+ y.appendCodePoint(c);
+ }
+ }
+ else if (0x00FFFF >= c) { // non-Ascii Bmp character (0x000080-0x00FFFF)
+ y.append("\\u" + String.format("%4H", c).replace(" ", "0"));
+ }
+ else { // non-Bmp character (0x010000-0x10FFFF)
+ int cc = c - 0x010000;
+ int ch = 0xD800 + (cc >> 10);
+ int cl = 0xDC00 + (cc & 0x03FF);
+ y.append("\\u" + String.format("%4H", ch).replace(" ", "0") + "\\u" + String.format("%4H", cl).replace(" ", "0"));
+ i++;
+ }
+ }
+ else {
+ if ('"' == c) {
+ y.append("\\\"");
+ }
+ else if ('\\' == c) {
+ y.append("\\\\");
+ }
+ else if ('/' == c) {
+ y.append("\\/");
+ }
+ else if ('\b' == c) {
+ y.append("\\b");
+ }
+ else if ('\f' == c) {
+ y.append("\\f");
+ }
+ else if ('\n' == c) {
+ y.append("\\n");
+ }
+ else if ('\r' == c) {
+ y.append("\\r");
+ }
+ else if ('\t' == c) {
+ y.append("\\t");
+ }
+ else if (0x00001F >= c) { // non-special control Ascii character (0x000000-0x00001F)
+ y.append("\\u" + String.format("%4H", c).replace(" ", "0"));
+ }
+ else {
+ y.appendCodePoint(c);
+ if (0x010000 <= c) {
+ i++;
+ }
+ }
+ }
+ }
+ return y.toString();
+ }
+
+ protected static String getUnescapedString(String x) throws MInvalidJsonValueException {
+ if (null == x) {
+ throw new IllegalArgumentException("Invalid 'x': null.");
+ }
+ //
+ StringBuilder y = new StringBuilder();
+ for (int i = 0; x.length() > i; i++) {
+ if (x.indexOf("\\\"", i) == i) {
+ y.append("\"");
+ i++;
+ }
+ else if (x.indexOf("\\\\", i) == i) {
+ y.append("\\");
+ i++;
+ }
+ else if (x.indexOf("\\/", i) == i) {
+ y.append("/");
+ i++;
+ }
+ else if (x.indexOf("\\b", i) == i) {
+ y.append("\b");
+ i++;
+ }
+ else if (x.indexOf("\\f", i) == i) {
+ y.append("\f");
+ i++;
+ }
+ else if (x.indexOf("\\n", i) == i) {
+ y.append("\n");
+ i++;
+ }
+ else if (x.indexOf("\\r", i) == i) {
+ y.append("\r");
+ i++;
+ }
+ else if (x.indexOf("\\t", i) == i) {
+ y.append("\t");
+ i++;
+ }
+ else if (x.indexOf("\\u", i) == i) {
+ if ((i + 2 + 4) > x.length()) {
+ throw new MInvalidJsonValueException(String.format("Invalid 'x': %s: not a Json string.", x));
+ }
+ else {
+ int ch = -1;
+ try {
+ ch = Integer.parseInt(x.substring(i + 2, i + 2 + 4), 16);
+ i += 5;
+ if ((0xD800 <= ch) && (0xDBFF >= ch)) {
+ i++;
+ if (x.indexOf("\\u", i) != i) {
+ throw new MInvalidJsonValueException(String.format("Invalid 'x': %s: not a Json string.", x));
+ }
+ else if ((i + 2 + 4) > x.length()) {
+ throw new MInvalidJsonValueException(String.format("Invalid 'x': %s: not a Json string.", x));
+ }
+ else {
+ int cl = -1;
+ try {
+ cl = Integer.parseInt(x.substring(i + 2, i + 2 + 4), 16);
+ i += 5;
+ if ((0xDC00 >= cl) || (0xDFFF <= cl)) {
+ throw new MInvalidJsonValueException(String.format("Invalid 'x': %s: not a Json string.", x));
+ }
+ int c = ((ch - 0xD800) << 10) + (cl - 0xDC00) + 0x010000;
+ y.appendCodePoint(c);
+ }
+ catch (NumberFormatException exception) {
+ throw new MInvalidJsonValueException(String.format("Invalid 'x': %s: not a Json string.", x)); // no need to propagate exception
+ }
+ }
+ }
+ else {
+ y.appendCodePoint(ch);
+ }
+ }
+ catch (NumberFormatException exception) {
+ throw new MInvalidJsonValueException(String.format("Invalid 'x': %s: not a Json string.", x)); // no need to propagate exception
+ }
+ }
+ }
+ else {
+ int c = x.codePointAt(i);
+ if (0x010000 <= c) {
+ i++;
+ }
+ y.appendCodePoint(c);
+ }
+ }
+ return y.toString();
+ }
+
+ /* Parsers. */
+
+ protected static int getTokenLength(String x) {
+ if (null == x) {
+ throw new IllegalArgumentException("Invalid 'x': null.");
+ }
+ //
+ if ((2 > x.length()) || ('"' != x.charAt(0))) {
+ return 0;
+ }
+ if ('"' == x.charAt(1)) {
+ return 1 + 1;
+ }
+ String y = x.substring(1);
+ int quotationMarkPosition = 0;
+ while (-1 < (quotationMarkPosition = y.indexOf("\"", quotationMarkPosition + 1))) {
+ int c = 0;
+ StringBuilder s = new StringBuilder("");
+ while ((c <= quotationMarkPosition) && (y.substring(quotationMarkPosition - c, quotationMarkPosition).equals(s.toString()))) {
+ c++;
+ s.append("\\");
+ }
+ c--;
+ if (0 == (c % 2)) {
+ break;
+ }
+ }
+ if (-1 == quotationMarkPosition) {
+ return 0;
+ }
+ return (quotationMarkPosition + 1) + 1;
+ }
+
+ public void parseString(String x) throws MInvalidJsonValueException {
+ if (MText.isBlank(x)) {
+ throw new IllegalArgumentException("Invalid 'x': null or empty.");
+ }
+ //
+ if ((2 > x.length()) || ('"' != x.charAt(0)) || ('"' != x.charAt(x.length() - 1))) {
+ throw new MInvalidJsonValueException(String.format("Invalid 'x': %s: not a Json string.", x));
+ }
+ String y = x.substring(1, x.length() - 1);
+ if (-1 < y.replaceAll("\\\\\"", "__").indexOf("\"")) {
+ throw new MInvalidJsonValueException(String.format("Invalid 'x': %s: not a Json string.", x));
+ }
+ this.setValue(MJsonString.getUnescapedString(y));
+ }
+
+ /* Formatter. */
+
+ @Override
+ public String getJsonValue() {
+ return "\"" + MJsonString.getEscapedString(this.getValue(), this.getExtendedEscapeMode()) + "\"";
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco.json;
+
+import com.marcozanon.macaco.MObject;
+
+public abstract class MJsonValue extends MObject {
+
+ /* */
+
+ @Override
+ public abstract MJsonValue clone();
+
+ /* Helpers. */
+
+ protected static boolean isWhitespace(int c) {
+ return ((' ' == c) || ('\t' == c) || ('\n' == c) || ('\r' == c));
+ }
+
+ protected static boolean isValueSeparator(int c) {
+ return (',' == c);
+ }
+
+ protected static boolean isNameSeparator(int c) {
+ return (':' == c);
+ }
+
+ protected static boolean isOpeningStructuralCharacter(int c) {
+ return (('[' == c) || ('{' == c));
+ }
+
+ protected static boolean isClosingStructuralCharacter(int c) {
+ return ((']' == c) || ('}' == c));
+ }
+
+ /* Formatter. */
+
+ public abstract String getJsonValue();
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco.logging;
+
+import com.marcozanon.macaco.database.MDatabaseConnection;
+import com.marcozanon.macaco.database.MDatabaseConnectionFailureException;
+import com.marcozanon.macaco.database.MDatabaseConnectionPool;
+import com.marcozanon.macaco.database.MSqlStatementException;
+import com.marcozanon.macaco.database.MSqlTable;
+import com.marcozanon.macaco.text.MText;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.LinkedHashMap;
+
+public class MLogDatabaseTable extends MLogTarget {
+
+ protected MDatabaseConnectionPool databaseConnectionPool = null;
+
+ protected String logDatabaseTable = null;
+ protected String logDatabaseTablePrimaryKey = null;
+ protected String logDatabaseField = null;
+
+ protected MDatabaseConnection databaseConnection = null;
+
+ /* */
+
+ public MLogDatabaseTable(MDatabaseConnectionPool databaseConnectionPool, String logDatabaseTable, String logDatabaseTablePrimaryKey, String logDatabaseField) throws MLoggingException {
+ super();
+ //
+ if (null == databaseConnectionPool) {
+ throw new IllegalArgumentException("Invalid 'databaseConnectionPool': null.");
+ }
+ if (MText.isEmpty(logDatabaseTable)) {
+ throw new IllegalArgumentException("Invalid 'logDatabaseTable': null or empty.");
+ }
+ if (MText.isEmpty(logDatabaseTablePrimaryKey)) {
+ throw new IllegalArgumentException("Invalid 'logDatabaseTablePrimaryKey': null or empty.");
+ }
+ if (MText.isEmpty(logDatabaseField)) {
+ throw new IllegalArgumentException("Invalid 'logDatabaseField': null or empty.");
+ }
+ //
+ this.databaseConnectionPool = databaseConnectionPool;
+ this.logDatabaseTable = logDatabaseTable;
+ this.logDatabaseTablePrimaryKey = logDatabaseTablePrimaryKey;
+ this.logDatabaseField = logDatabaseField;
+ }
+
+ @Override
+ public void close() throws MLoggingException {
+ }
+
+ /* Database connection pool. */
+
+ protected MDatabaseConnectionPool getDatabaseConnectionPool() {
+ return this.databaseConnectionPool;
+ }
+
+ /* Database logging parameters. */
+
+ protected String getLogDatabaseTable() {
+ return this.logDatabaseTable;
+ }
+
+ protected String getLogDatabaseTablePrimaryKey() {
+ return this.logDatabaseTablePrimaryKey;
+ }
+
+ protected String getLogDatabaseField() {
+ return this.logDatabaseField;
+ }
+
+ /* Output. */
+
+ @Override
+ public void appendMessage(String message) throws MLoggingException {
+ this.appendMessage(message, 0);
+ }
+
+ @Override
+ public void appendMessage(String message, int indentation) throws MLoggingException {
+ if (null == message) {
+ throw new IllegalArgumentException("Invalid 'message': null.");
+ }
+ if (0 > indentation) {
+ throw new IllegalArgumentException(String.format("Invalid 'indentation': %s: cannot be negative.", indentation));
+ }
+ //
+ try {
+ String timestamp = (new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")).format(new Date());
+ //
+ StringBuilder separator = new StringBuilder(" ");
+ for (int i = 0; i < indentation; i++) {
+ separator.append(" ");
+ }
+ //
+ MDatabaseConnection databaseConnection = this.getDatabaseConnectionPool().popDatabaseConnection();
+ //
+ LinkedHashMap<String, Object> p = new LinkedHashMap<String, Object>();
+ p.put(this.getLogDatabaseField(), timestamp + separator + message);
+ (new MSqlTable(databaseConnection, this.getLogDatabaseTable(), this.getLogDatabaseTablePrimaryKey())).setRecord(p, null, false);
+ //
+ this.getDatabaseConnectionPool().pushDatabaseConnection(databaseConnection);
+ }
+ catch (MDatabaseConnectionFailureException exception) {
+ throw new MLoggingException("Could not write to database table.", exception);
+ }
+ catch (MSqlStatementException exception) {
+ throw new MLoggingException("Could not write to database table.", exception);
+ }
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco.logging;
+
+import com.marcozanon.macaco.MObject;
+import java.util.LinkedList;
+
+public class MLogFilter extends MObject {
+
+ public static enum Threshold {
+ STANDARD,
+ DEBUG
+ };
+
+ protected MLogFilter.Threshold threshold = null;
+
+ protected LinkedList<MLogTarget> logTargets = new LinkedList<MLogTarget>();
+
+ protected boolean pausedState = false;
+ protected LinkedList<MLogMessage> logMessageQueue = new LinkedList<MLogMessage>();
+
+ /* */
+
+ public MLogFilter(MLogFilter.Threshold threshold) {
+ super();
+ //
+ this.setThreshold(threshold);
+ }
+
+ public void close() throws MLoggingException {
+ for (MLogTarget t: this.getLogTargets()) {
+ t.close();
+ }
+ }
+
+ @Override
+ protected void finalize() {
+ try {
+ this.close();
+ }
+ catch (Exception exception) {
+ }
+ }
+
+ /* Threshold. */
+
+ public void setThreshold(MLogFilter.Threshold threshold) {
+ if (null == threshold) {
+ throw new IllegalArgumentException("Invalid 'threshold': null.");
+ }
+ //
+ this.threshold = threshold;
+ }
+
+ public MLogFilter.Threshold getThreshold() {
+ return this.threshold;
+ }
+
+ /* Log targets. */
+
+ public void addLogTarget(MLogTarget logTarget) {
+ if (null == logTarget) {
+ throw new IllegalArgumentException("Invalid 'logTarget': null.");
+ }
+ //
+ synchronized (this.logTargets) {
+ this.logTargets.add(logTarget);
+ }
+ }
+
+ protected LinkedList<MLogTarget> getLogTargets() {
+ return this.logTargets;
+ }
+
+ /* Output. */
+
+ public void setPausedState(boolean pausedState) throws MLoggingException {
+ this.pausedState = pausedState;
+ //
+ if (!this.getPausedState()) {
+ this.flushMessages();
+ }
+ }
+
+ public boolean getPausedState() {
+ return this.pausedState;
+ }
+
+ public void appendMessage(MLogFilter.Threshold level, String message) throws MLoggingException {
+ this.appendMessage(level, message, 0);
+ }
+
+ public void appendMessage(MLogFilter.Threshold level, String message, int indentation) throws MLoggingException {
+ if (null == level) {
+ throw new IllegalArgumentException("Invalid 'level': null.");
+ }
+ //
+ if (level.ordinal() > this.getThreshold().ordinal()) {
+ return;
+ }
+ //
+ this.logMessageQueue.add(new MLogMessage(message, indentation));
+ //
+ if (!this.getPausedState()) {
+ this.flushMessages();
+ }
+ }
+
+ protected void flushMessages() throws MLoggingException {
+ while (0 < this.logMessageQueue.size()) {
+ MLogMessage logMessage = this.logMessageQueue.remove();
+ String message = logMessage.getMessage();
+ int indentation = logMessage.getIndentation();
+ //
+ for (MLogTarget logTarget: this.getLogTargets()) {
+ logTarget.appendMessage(message, indentation);
+ }
+ }
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco.logging;
+
+public interface MLogListener {
+
+ /* Logging. */
+
+ public void onMessageLogging(String message);
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco.logging;
+
+import com.marcozanon.macaco.MObject;
+
+public class MLogMessage extends MObject {
+
+ protected String message = null;
+ protected int indentation = 0;
+
+ /* */
+
+ public MLogMessage(String message, int indentation) {
+ if (null == message) {
+ throw new IllegalArgumentException("Invalid 'message': null.");
+ }
+ if (0 > indentation) {
+ throw new IllegalArgumentException(String.format("Invalid 'indentation': %s: cannot be negative.", indentation));
+ }
+ //
+ this.message = message;
+ this.indentation = indentation;
+ }
+
+ /* Values. */
+
+ public String getMessage() {
+ return this.message;
+ }
+
+ public int getIndentation() {
+ return this.indentation;
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco.logging;
+
+import com.marcozanon.macaco.MConstants;
+import com.marcozanon.macaco.text.MText;
+import java.io.BufferedWriter;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+public class MLogPlainTextFile extends MLogTarget {
+
+ protected String file = null;
+
+ protected BufferedWriter buffer = null;
+
+ /* */
+
+ public MLogPlainTextFile(String file) throws MLoggingException {
+ super();
+ //
+ if (MText.isBlank(file)) {
+ throw new IllegalArgumentException("Invalid 'file': null or empty.");
+ }
+ //
+ this.file = file;
+ try {
+ this.buffer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(this.getFile(), true), MConstants.TEXT_ENCODING));
+ }
+ catch (FileNotFoundException exception) {
+ throw new MLoggingException("Could not open file.", exception);
+ }
+ catch (UnsupportedEncodingException exception) { // cannot happen
+ }
+ }
+
+ @Override
+ public void close() throws MLoggingException {
+ try {
+ this.getBuffer().close();
+ }
+ catch (IOException exception) {
+ throw new MLoggingException("Could not close file.", exception);
+ }
+ }
+
+ /* File. */
+
+ protected String getFile() {
+ return this.file;
+ }
+
+ /* Buffer. */
+
+ protected BufferedWriter getBuffer() {
+ return this.buffer;
+ }
+
+ /* Output. */
+
+ @Override
+ public void appendMessage(String message) throws MLoggingException {
+ this.appendMessage(message, 0);
+ }
+
+ @Override
+ public void appendMessage(String message, int indentation) throws MLoggingException {
+ if (null == message) {
+ throw new IllegalArgumentException("Invalid 'message': null.");
+ }
+ if (0 > indentation) {
+ throw new IllegalArgumentException(String.format("Invalid 'indentation': %s: cannot be negative.", indentation));
+ }
+ //
+ try {
+ String timestamp = (new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")).format(new Date());
+ //
+ StringBuilder separator = new StringBuilder(" ");
+ for (int i = 0; i < indentation; i++) {
+ separator.append(" ");
+ }
+ //
+ synchronized (this.getBuffer()) {
+ this.getBuffer().write(timestamp + separator + message);
+ this.getBuffer().newLine();
+ this.getBuffer().flush();
+ }
+ }
+ catch (IOException exception) {
+ throw new MLoggingException("Could not write to file.", exception);
+ }
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco.logging;
+
+import com.marcozanon.macaco.MObject;
+
+public abstract class MLogTarget extends MObject {
+
+ /* */
+
+ public abstract void close() throws MLoggingException;
+
+ @Override
+ protected void finalize() {
+ try {
+ this.close();
+ }
+ catch (Exception exception) {
+ }
+ }
+
+ /* Output. */
+
+ public abstract void appendMessage(String message) throws MLoggingException;
+
+ public abstract void appendMessage(String message, int indentation) throws MLoggingException;
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco.logging;
+
+import com.marcozanon.macaco.MException;
+
+@SuppressWarnings("serial")
+public class MLoggingException extends MException {
+
+ /* */
+
+ public MLoggingException() {
+ super();
+ }
+
+ public MLoggingException(String message) {
+ super(message);
+ }
+
+ public MLoggingException(Throwable error) {
+ super(error);
+ }
+
+ public MLoggingException(String message, Throwable error) {
+ super(message, error);
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco.text;
+
+import com.marcozanon.macaco.MConstants;
+import com.marcozanon.macaco.MObject;
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.StringJoiner;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import org.xml.sax.Attributes;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.DefaultHandler;
+
+public class MText extends MObject {
+
+ /* Checks. */
+
+ public static boolean isEmpty(String x) {
+ if ((null != x) && ("".equals(x))) {
+ return true;
+ }
+ return false;
+ }
+
+ public static boolean isBlank(String x) {
+ if ((null == x) || ("".equals(x))) {
+ return true;
+ }
+ return false;
+ }
+
+ /* Utility methods. */
+
+ public static String repeat(String x, int times) {
+ if (1 > times) {
+ throw new IllegalArgumentException(String.format("Invalid 'times': %s.", times));
+ }
+ //
+ StringBuilder y = new StringBuilder("");
+ for (int i = 0; i < times; i++) {
+ y = y.append(x);
+ }
+ return y.toString();
+ }
+
+ public static String getRandomToken(int length) {
+ if (0 > length) {
+ throw new IllegalArgumentException(String.format("Invalid 'length': %s.", length));
+ }
+ //
+ StringBuilder y = new StringBuilder("");
+ for (int i = 0; i < length; i++) {
+ char c = (char)(65 + (25 * Math.random()));
+ y = y.append(c);
+ }
+ return y.toString();
+ }
+
+ public static String removeLine(String x, int lineNumber) {
+ if (null == x) {
+ throw new IllegalArgumentException("Invalid 'x': null.");
+ }
+ //
+ String[] xLines = x.split(System.getProperty("line.separator"));
+ //
+ if ((0 > lineNumber) || (xLines.length - 1 < lineNumber)) {
+ throw new IllegalArgumentException(String.format("Invalid 'lineNumber': %s.", lineNumber));
+ }
+ //
+ StringJoiner stringJoiner = new StringJoiner(System.getProperty("line.separator"));
+ for (int l = 0; xLines.length > l; l++) {
+ if (l == lineNumber) {
+ continue;
+ }
+ //
+ stringJoiner.add(xLines[l]);
+ }
+ //
+ return stringJoiner.toString();
+ }
+
+ public static String removeFirstLine(String x) {
+ return MText.removeLine(x, 0);
+ }
+
+ public static String removeLastLine(String x) {
+ if (null == x) {
+ throw new IllegalArgumentException("Invalid 'x': null.");
+ }
+ //
+ String[] xLines = x.split(System.getProperty("line.separator"));
+ //
+ return MText.removeLine(x, xLines.length - 1);
+ }
+
+ /* Javascript escape. */
+
+ public static String getJavascriptEscapedString(String x) {
+ if (null == x) {
+ return null;
+ }
+ //
+ String text = x;
+ //
+ text = text.replace("\\", "\\\\");
+ text = text.replace("'", "\\\'");
+ //
+ return text;
+ }
+
+ /* Xhtml escape and safety. */
+
+ public static String getXhtmlEscapedString(String x) { // similar to PHP's htmlspecialchars()
+ if (null == x) {
+ return null;
+ }
+ //
+ String text = x;
+ //
+ text = text.replace("&", "&");
+ text = text.replace("\"", """);
+ text = text.replace("'", "'");
+ text = text.replace("<", "<");
+ text = text.replace(">", ">");
+ //
+ text = text.replace("\n", "<br />");
+ //
+ return text;
+ }
+
+ public static String getXhtmlNumericEntitiesString(String x) { // for compatibility with innerHTML
+ if (null == x) {
+ return null;
+ }
+ //
+ String text = x;
+ //
+ text = text.replace(" ", " ");
+ text = text.replace("¡", "¡");
+ text = text.replace("¢", "¢");
+ text = text.replace("£", "£");
+ text = text.replace("¤", "¤");
+ text = text.replace("¥", "¥");
+ text = text.replace("¦", "¦");
+ text = text.replace("§", "§");
+ text = text.replace("¨", "¨");
+ text = text.replace("©", "©");
+ text = text.replace("ª", "ª");
+ text = text.replace("«", "«");
+ text = text.replace("¬", "¬");
+ text = text.replace("­", "­");
+ text = text.replace("®", "®");
+ text = text.replace("¯", "¯");
+ text = text.replace("°", "°");
+ text = text.replace("±", "±");
+ text = text.replace("²", "²");
+ text = text.replace("³", "³");
+ text = text.replace("´", "´");
+ text = text.replace("µ", "µ");
+ text = text.replace("¶", "¶");
+ text = text.replace("·", "·");
+ text = text.replace("¸", "¸");
+ text = text.replace("¹", "¹");
+ text = text.replace("º", "º");
+ text = text.replace("»", "»");
+ text = text.replace("¼", "¼");
+ text = text.replace("½", "½");
+ text = text.replace("¾", "¾");
+ text = text.replace("¿", "¿");
+ text = text.replace("À", "À");
+ text = text.replace("Á", "Á");
+ text = text.replace("Â", "Â");
+ text = text.replace("Ã", "Ã");
+ text = text.replace("Ä", "Ä");
+ text = text.replace("Å", "Å");
+ text = text.replace("Æ", "Æ");
+ text = text.replace("Ç", "Ç");
+ text = text.replace("È", "È");
+ text = text.replace("É", "É");
+ text = text.replace("Ê", "Ê");
+ text = text.replace("Ë", "Ë");
+ text = text.replace("Ì", "Ì");
+ text = text.replace("Í", "Í");
+ text = text.replace("Î", "Î");
+ text = text.replace("Ï", "Ï");
+ text = text.replace("Ð", "Ð");
+ text = text.replace("Ñ", "Ñ");
+ text = text.replace("Ò", "Ò");
+ text = text.replace("Ó", "Ó");
+ text = text.replace("Ô", "Ô");
+ text = text.replace("Õ", "Õ");
+ text = text.replace("Ö", "Ö");
+ text = text.replace("×", "×");
+ text = text.replace("Ø", "Ø");
+ text = text.replace("Ù", "Ù");
+ text = text.replace("Ú", "Ú");
+ text = text.replace("Û", "Û");
+ text = text.replace("Ü", "Ü");
+ text = text.replace("Ý", "Ý");
+ text = text.replace("Þ", "Þ");
+ text = text.replace("ß", "ß");
+ text = text.replace("à", "à");
+ text = text.replace("á", "á");
+ text = text.replace("â", "â");
+ text = text.replace("ã", "ã");
+ text = text.replace("ä", "ä");
+ text = text.replace("å", "å");
+ text = text.replace("æ", "æ");
+ text = text.replace("ç", "ç");
+ text = text.replace("è", "è");
+ text = text.replace("é", "é");
+ text = text.replace("ê", "ê");
+ text = text.replace("ë", "ë");
+ text = text.replace("ì", "ì");
+ text = text.replace("í", "í");
+ text = text.replace("î", "î");
+ text = text.replace("ï", "ï");
+ text = text.replace("ð", "ð");
+ text = text.replace("ñ", "ñ");
+ text = text.replace("ò", "ò");
+ text = text.replace("ó", "ó");
+ text = text.replace("ô", "ô");
+ text = text.replace("õ", "õ");
+ text = text.replace("ö", "ö");
+ text = text.replace("÷", "÷");
+ text = text.replace("ø", "ø");
+ text = text.replace("ù", "ù");
+ text = text.replace("ú", "ú");
+ text = text.replace("û", "û");
+ text = text.replace("ü", "ü");
+ text = text.replace("ý", "ý");
+ text = text.replace("þ", "þ");
+ text = text.replace("ÿ", "ÿ");
+ text = text.replace("Œ", "Œ");
+ text = text.replace("œ", "œ");
+ text = text.replace("Š", "Š");
+ text = text.replace("š", "š");
+ text = text.replace("Ÿ", "Ÿ");
+ text = text.replace("ƒ", "ƒ");
+ text = text.replace("ˆ", "ˆ");
+ text = text.replace("˜", "˜");
+ text = text.replace("Α", "Α");
+ text = text.replace("Β", "Β");
+ text = text.replace("Γ", "Γ");
+ text = text.replace("Δ", "Δ");
+ text = text.replace("Ε", "Ε");
+ text = text.replace("Ζ", "Ζ");
+ text = text.replace("Η", "Η");
+ text = text.replace("Θ", "Θ");
+ text = text.replace("Ι", "Ι");
+ text = text.replace("Κ", "Κ");
+ text = text.replace("Λ", "Λ");
+ text = text.replace("Μ", "Μ");
+ text = text.replace("Ν", "Ν");
+ text = text.replace("Ξ", "Ξ");
+ text = text.replace("Ο", "Ο");
+ text = text.replace("Π", "Π");
+ text = text.replace("Ρ", "Ρ");
+ text = text.replace("Σ", "Σ");
+ text = text.replace("Τ", "Τ");
+ text = text.replace("Υ", "Υ");
+ text = text.replace("Φ", "Φ");
+ text = text.replace("Χ", "Χ");
+ text = text.replace("Ψ", "Ψ");
+ text = text.replace("Ω", "Ω");
+ text = text.replace("α", "α");
+ text = text.replace("β", "β");
+ text = text.replace("γ", "γ");
+ text = text.replace("δ", "δ");
+ text = text.replace("ε", "ε");
+ text = text.replace("ζ", "ζ");
+ text = text.replace("η", "η");
+ text = text.replace("θ", "θ");
+ text = text.replace("ι", "ι");
+ text = text.replace("κ", "κ");
+ text = text.replace("λ", "λ");
+ text = text.replace("μ", "μ");
+ text = text.replace("ν", "ν");
+ text = text.replace("ξ", "ξ");
+ text = text.replace("ο", "ο");
+ text = text.replace("π", "π");
+ text = text.replace("ρ", "ρ");
+ text = text.replace("ς", "ς");
+ text = text.replace("σ", "σ");
+ text = text.replace("τ", "τ");
+ text = text.replace("υ", "υ");
+ text = text.replace("φ", "φ");
+ text = text.replace("χ", "χ");
+ text = text.replace("ψ", "ψ");
+ text = text.replace("ω", "ω");
+ text = text.replace("ϑ", "ϑ");
+ text = text.replace("ϒ", "ϒ");
+ text = text.replace("ϖ", "ϖ");
+ text = text.replace(" ", " ");
+ text = text.replace(" ", " ");
+ text = text.replace(" ", " ");
+ text = text.replace("‌", "‌");
+ text = text.replace("‍", "‍");
+ text = text.replace("‎", "‎");
+ text = text.replace("‏", "‏");
+ text = text.replace("–", "–");
+ text = text.replace("—", "—");
+ text = text.replace("‘", "‘");
+ text = text.replace("’", "’");
+ text = text.replace("‚", "‚");
+ text = text.replace("“", "“");
+ text = text.replace("”", "”");
+ text = text.replace("„", "„");
+ text = text.replace("†", "†");
+ text = text.replace("‡", "‡");
+ text = text.replace("•", "•");
+ text = text.replace("…", "…");
+ text = text.replace("‰", "‰");
+ text = text.replace("′", "′");
+ text = text.replace("″", "″");
+ text = text.replace("‹", "‹");
+ text = text.replace("›", "›");
+ text = text.replace("‾", "‾");
+ text = text.replace("⁄", "⁄");
+ text = text.replace("€", "€");
+ text = text.replace("ℑ", "ℑ");
+ text = text.replace("℘", "℘");
+ text = text.replace("ℜ", "ℜ");
+ text = text.replace("™", "™");
+ text = text.replace("ℵ", "ℵ");
+ text = text.replace("←", "←");
+ text = text.replace("↑", "↑");
+ text = text.replace("→", "→");
+ text = text.replace("↓", "↓");
+ text = text.replace("↔", "↔");
+ text = text.replace("↵", "↵");
+ text = text.replace("⇐", "⇐");
+ text = text.replace("⇑", "⇑");
+ text = text.replace("⇒", "⇒");
+ text = text.replace("⇓", "⇓");
+ text = text.replace("⇔", "⇔");
+ text = text.replace("∀", "∀");
+ text = text.replace("∂", "∂");
+ text = text.replace("∃", "∃");
+ text = text.replace("∅", "∅");
+ text = text.replace("∇", "∇");
+ text = text.replace("∈", "∈");
+ text = text.replace("∉", "∉");
+ text = text.replace("∋", "∋");
+ text = text.replace("∏", "∏");
+ text = text.replace("∑", "∑");
+ text = text.replace("−", "−");
+ text = text.replace("∗", "∗");
+ text = text.replace("√", "√");
+ text = text.replace("∝", "∝");
+ text = text.replace("∞", "∞");
+ text = text.replace("∠", "∠");
+ text = text.replace("∧", "∧");
+ text = text.replace("∨", "∨");
+ text = text.replace("∩", "∩");
+ text = text.replace("∪", "∪");
+ text = text.replace("∫", "∫");
+ text = text.replace("∴", "∴");
+ text = text.replace("∼", "∼");
+ text = text.replace("≅", "≅");
+ text = text.replace("≈", "≈");
+ text = text.replace("≠", "≠");
+ text = text.replace("≡", "≡");
+ text = text.replace("≤", "≤");
+ text = text.replace("≥", "≥");
+ text = text.replace("⊂", "⊂");
+ text = text.replace("⊃", "⊃");
+ text = text.replace("⊄", "⊄");
+ text = text.replace("⊆", "⊆");
+ text = text.replace("⊇", "⊇");
+ text = text.replace("⊕", "⊕");
+ text = text.replace("⊗", "⊗");
+ text = text.replace("⊥", "⊥");
+ text = text.replace("⋅", "⋅");
+ text = text.replace("⌈", "⌈");
+ text = text.replace("⌉", "⌉");
+ text = text.replace("⌊", "⌊");
+ text = text.replace("⌋", "⌋");
+ text = text.replace("⟨", "〈");
+ text = text.replace("⟩", "〉");
+ text = text.replace("◊", "◊");
+ text = text.replace("♠", "♠");
+ text = text.replace("♣", "♣");
+ text = text.replace("♥", "♥");
+ text = text.replace("♦", "♦");
+ //
+ return text;
+ }
+
+ public static String getXhtmlSafeString(String x) throws MXhtmlUnsafeStringException {
+ if (null == x) {
+ return null;
+ }
+ //
+ String text = x;
+ //
+ StringBuilder fakeXhtmlPageContent = new StringBuilder("");
+ fakeXhtmlPageContent.append(String.format("<?xml version=\"1.0\" encoding=\"%s\" ?>", MConstants.TEXT_ENCODING));
+ fakeXhtmlPageContent.append("<!DOCTYPE html SYSTEM \"fake-dtd\" >");
+ fakeXhtmlPageContent.append("<html xmlns=\"http://www.w3.org/1999/xhtml\">");
+ fakeXhtmlPageContent.append("<head><title /></head>");
+ fakeXhtmlPageContent.append(String.format("<body>%s</body>", text));
+ fakeXhtmlPageContent.append("</html>");
+ // Validate Xhtml (with no dangerous tags and event attributes).
+ try {
+ SAXParserFactory factory = SAXParserFactory.newInstance();
+ factory.setValidating(true);
+ factory.setNamespaceAware(true);
+ SAXParser parser = factory.newSAXParser();
+ XMLReader reader = parser.getXMLReader();
+ reader.setEntityResolver(new EntityResolver() {
+
+ @Override
+ public InputSource resolveEntity(String publicId, String systemId) {
+ return new InputSource(new BufferedInputStream(this.getClass().getClassLoader().getResourceAsStream("dtd/xhtml1-transitional-macaco-edit.dtd")));
+ }
+
+ });
+ reader.setContentHandler(new DefaultHandler() {
+
+ @Override
+ public void startElement(String namespaceUri, String strippedName, String tagName, Attributes attributes) throws SAXException {
+ if ("script".equalsIgnoreCase(tagName)) {
+ throw new SAXException(String.format("Tag not allowed: %s.", tagName));
+ }
+ for (int a = 0; attributes.getLength() > a; a++) {
+ if (attributes.getLocalName(a).toLowerCase().startsWith("on")) {
+ throw new SAXException(String.format("Attribute not allowed: %s.", attributes.getLocalName(a)));
+ }
+ }
+ }
+
+ });
+ reader.setErrorHandler(new ErrorHandler() {
+
+ @Override
+ public void error(SAXParseException exception) throws SAXException {
+ throw new SAXException(exception);
+ }
+
+ @Override
+ public void fatalError(SAXParseException exception) throws SAXException {
+ throw new SAXException(exception);
+ }
+
+ @Override
+ public void warning(SAXParseException exception) throws SAXException {
+ throw new SAXException(exception);
+ }
+
+ });
+ //
+ reader.parse(new InputSource(new ByteArrayInputStream(fakeXhtmlPageContent.toString().getBytes(MConstants.TEXT_ENCODING))));
+ }
+ catch (ParserConfigurationException exception) { // cannot happen
+ }
+ catch (SAXException exception) {
+ throw new MXhtmlUnsafeStringException("Invalid 'x': unsafe tags or attributes inside.", exception);
+ }
+ catch (UnsupportedEncodingException exception) { // cannot happen
+ }
+ catch (IOException exception) { // cannot happen; put here not to bypass UnsupportedEncodingException
+ }
+ // Also convert named entities to numeric entities.
+ return MText.getXhtmlNumericEntitiesString(text);
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco.text;
+
+import com.marcozanon.macaco.MException;
+
+public abstract class MTextException extends MException {
+
+ /* */
+
+ public MTextException() {
+ super();
+ }
+
+ public MTextException(String message) {
+ super(message);
+ }
+
+ public MTextException(Throwable error) {
+ super(error);
+ }
+
+ public MTextException(String message, Throwable error) {
+ super(message, error);
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco.text;
+
+public abstract class MTranslationException extends MTextException {
+
+ /* */
+
+ public MTranslationException() {
+ super();
+ }
+
+ public MTranslationException(String message) {
+ super(message);
+ }
+
+ public MTranslationException(Throwable error) {
+ super(error);
+ }
+
+ public MTranslationException(String message, Throwable error) {
+ super(message, error);
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco.text;
+
+@SuppressWarnings("serial")
+public class MTranslationFileParsingException extends MTranslationException {
+
+ /* */
+
+ public MTranslationFileParsingException() {
+ super();
+ }
+
+ public MTranslationFileParsingException(String message) {
+ super(message);
+ }
+
+ public MTranslationFileParsingException(Throwable error) {
+ super(error);
+ }
+
+ public MTranslationFileParsingException(String message, Throwable error) {
+ super(message, error);
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco.text;
+
+@SuppressWarnings("serial")
+public class MTranslationValueNotFoundException extends MTranslationException {
+
+ /* */
+
+ public MTranslationValueNotFoundException() {
+ super();
+ }
+
+ public MTranslationValueNotFoundException(String message) {
+ super(message);
+ }
+
+ public MTranslationValueNotFoundException(Throwable error) {
+ super(error);
+ }
+
+ public MTranslationValueNotFoundException(String message, Throwable error) {
+ super(message, error);
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco.text;
+
+import com.marcozanon.macaco.MConstants;
+import com.marcozanon.macaco.MObject;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.io.LineNumberReader;
+import java.io.UnsupportedEncodingException;
+import java.util.LinkedHashMap;
+import java.util.Locale;
+
+public class MTranslator extends MObject {
+
+ protected Locale basicLocale = null;
+
+ protected LinkedHashMap<String, LinkedHashMap<String, String>> messages = new LinkedHashMap<String, LinkedHashMap<String, String>>();
+
+ /* */
+
+ public MTranslator(Locale basicLocale) {
+ super();
+ //
+ if (null == basicLocale) {
+ throw new IllegalArgumentException("Invalid 'basicLocale': null.");
+ }
+ //
+ this.basicLocale = basicLocale;
+ }
+
+ public MTranslator(Locale basicLocale, LinkedHashMap<String, LinkedHashMap<String, String>> messages) {
+ this(basicLocale);
+ //
+ this.messages = messages;
+ }
+
+ public MTranslator(Locale basicLocale, String file) throws MTranslationFileParsingException {
+ this(basicLocale);
+ //
+ this.parseFile(file);
+ }
+
+ @Override
+ public MTranslator clone() {
+ return new MTranslator(this.getBasicLocale(), this.getMessages());
+ }
+
+ /* Locale. */
+
+ protected Locale getBasicLocale() {
+ return this.basicLocale;
+ }
+
+ /* String management. */
+
+ protected LinkedHashMap<String, LinkedHashMap<String, String>> getMessages() {
+ return this.messages;
+ }
+
+ public void parseFile(String file) throws MTranslationFileParsingException {
+ if (MText.isBlank(file)) {
+ throw new IllegalArgumentException("Invalid 'file': null or empty.");
+ }
+ //
+ try (LineNumberReader buffer = new LineNumberReader(new InputStreamReader(new FileInputStream(file), MConstants.TEXT_ENCODING))) {
+ String message = null;
+ String line = null;
+ synchronized (this.getMessages()) {
+ while (true) {
+ try {
+ line = buffer.readLine();
+ }
+ catch (IOException exception) {
+ throw new MTranslationFileParsingException("Could not read file.", exception);
+ }
+ if (null == line) {
+ break;
+ }
+ line = line.trim();
+ if (MText.isEmpty(line)) {
+ message = null;
+ continue;
+ }
+ else if ((line.startsWith("#")) || (line.startsWith(";"))) {
+ continue;
+ }
+ else if (null == message) {
+ message = line;
+ if (this.getMessages().containsKey(message)) {
+ throw new MTranslationFileParsingException(String.format("Invalid line: %s: duplicated message: %s.", buffer.getLineNumber(), message));
+ }
+ this.getMessages().put(message, new LinkedHashMap<String, String>());
+ }
+ else {
+ int a = line.indexOf("=");
+ if (-1 == a) {
+ throw new MTranslationFileParsingException(String.format("Invalid line: %s: string malformed: %s.", buffer.getLineNumber(), line));
+ }
+ String localeRepresentation = line.substring(0, a).trim();
+ String translation = line.substring(a + 1).trim();
+ if (this.getMessages().get(message).containsKey(localeRepresentation)) {
+ throw new MTranslationFileParsingException(String.format("Invalid line: %s: duplicated translation for locale: %s.", buffer.getLineNumber(), message));
+ }
+ this.getMessages().get(message).put(localeRepresentation, translation);
+ }
+ }
+ }
+ }
+ catch (FileNotFoundException exception) {
+ throw new MTranslationFileParsingException("Could not open file.", exception);
+ }
+ catch (UnsupportedEncodingException exception) { // cannot happen
+ }
+ catch (IOException exception) {
+ throw new MTranslationFileParsingException("Could not open / close file.", exception);
+ }
+ }
+
+ public void clear() {
+ synchronized (this.getMessages()) {
+ this.getMessages().clear();
+ }
+ }
+
+ public String t(String message, Locale locale) {
+ return this.getLenientTranslation(message, locale);
+ }
+
+ public String getLenientTranslation(String message, Locale locale) {
+ String translation = null;
+ try {
+ translation = this.getTranslation(message, locale, false);
+ }
+ catch (MTranslationValueNotFoundException exception) { // cannot happen
+ }
+ return translation;
+ }
+
+ public String getStrictTranslation(String message, Locale locale) throws MTranslationValueNotFoundException {
+ return this.getTranslation(message, locale, true);
+ }
+
+ protected String getTranslation(String message, Locale locale, boolean strictMode) throws MTranslationValueNotFoundException {
+ if (MText.isBlank(message)) {
+ throw new IllegalArgumentException("Invalid 'message': null or empty.");
+ }
+ if (null == locale) {
+ throw new IllegalArgumentException("Invalid 'locale': null.");
+ }
+ //
+ if (!this.getMessages().containsKey(message)) {
+ if (strictMode) {
+ throw new MTranslationValueNotFoundException(String.format("Invalid 'message': %s: not available.", message));
+ }
+ return message;
+ }
+/*
+ if (this.getBasicLocale().equals(locale)) {
+ return message;
+ }
+*/
+ LinkedHashMap<String, String> messageTranslations = this.getMessages().get(message);
+ String localeRepresentation = locale.toString();
+ if (!messageTranslations.containsKey(localeRepresentation)) {
+ if (strictMode) {
+ throw new MTranslationValueNotFoundException(String.format("Invalid 'locale': %s: translation not available for message: %s.", localeRepresentation, message));
+ }
+ String localeFallbackRepresentation = locale.getLanguage();
+ if (!messageTranslations.containsKey(localeFallbackRepresentation)) {
+ return message;
+ }
+ return messageTranslations.get(localeFallbackRepresentation);
+ }
+ return messageTranslations.get(localeRepresentation);
+ }
+
+}
--- /dev/null
+/**
+ * Macaco
+ * Copyright (c) 2009-2022 Marco Zanon <info@marcozanon.com>.
+ * See LICENSE for details.
+ */
+
+package com.marcozanon.macaco.text;
+
+@SuppressWarnings("serial")
+public class MXhtmlUnsafeStringException extends MTextException {
+
+ /* */
+
+ public MXhtmlUnsafeStringException() {
+ super();
+ }
+
+ public MXhtmlUnsafeStringException(String message) {
+ super(message);
+ }
+
+ public MXhtmlUnsafeStringException(Throwable error) {
+ super(error);
+ }
+
+ public MXhtmlUnsafeStringException(String message, Throwable error) {
+ super(message, error);
+ }
+
+}