From 713ee6daef338045052ea2d86ec21f330d658fea Mon Sep 17 00:00:00 2001 From: Dawid Dworak Date: Mon, 8 Jul 2024 07:53:33 +0200 Subject: [PATCH 01/14] Remove obscure modules and update dependencies --- build.sbt | 29 +--- scex-java-test/src/main/java/JavaCostam.java | 11 -- .../src/main/java/ScexJavaTest.java | 69 --------- .../src/main/resources/symbolAttributes.scala | 11 -- .../src/main/resources/symbolValidator.scala | 62 -------- .../src/main/resources/syntaxValidator.scala | 17 --- .../scala/vaadin/CompletionPlayground.scala | 141 ------------------ scex-test/src/main/scala/vaadin/Dyn.scala | 27 ---- scex-test/src/main/scala/vaadin/JavaLol.java | 31 ---- scex-test/src/main/scala/vaadin/JavaRoot.java | 19 --- scex-test/src/main/scala/vaadin/Root.scala | 25 ---- 11 files changed, 6 insertions(+), 436 deletions(-) delete mode 100644 scex-java-test/src/main/java/JavaCostam.java delete mode 100644 scex-java-test/src/main/java/ScexJavaTest.java delete mode 100644 scex-java-test/src/main/resources/symbolAttributes.scala delete mode 100644 scex-java-test/src/main/resources/symbolValidator.scala delete mode 100644 scex-java-test/src/main/resources/syntaxValidator.scala delete mode 100644 scex-test/src/main/scala/vaadin/CompletionPlayground.scala delete mode 100644 scex-test/src/main/scala/vaadin/Dyn.scala delete mode 100644 scex-test/src/main/scala/vaadin/JavaLol.java delete mode 100644 scex-test/src/main/scala/vaadin/JavaRoot.java delete mode 100644 scex-test/src/main/scala/vaadin/Root.scala diff --git a/build.sbt b/build.sbt index d8d41682..de155762 100644 --- a/build.sbt +++ b/build.sbt @@ -23,16 +23,14 @@ val CompileAndTest = "compile->compile;test->test" val parserCombinatorsVersion = "2.4.0" val avsCommonsVersion = "2.16.0" -val jettyVersion = "9.4.54.v20240208" // Tests only -val vaadinVersion = "6.8.18" // Tests only val slf4jVersion = "2.0.13" val logbackVersion = "1.5.6" // Tests only val commonsLang3Version = "3.14.0" val commonsCodecVersion = "1.17.0" -val guavaVersion = "33.2.0-jre" -val commonsNetVersion = "3.10.0" +val guavaVersion = "33.2.1-jre" +val commonsNetVersion = "3.11.1" val jodaTimeVersion = "2.12.7" -val scalatestVersion = "3.2.18" +val scalatestVersion = "3.2.19" val noPublishSettings = Seq( publish / skip := true @@ -106,7 +104,7 @@ lazy val subprojectSettings = Seq( ) lazy val scex = project.in(file(".")) - .aggregate(`scex-macros`, `scex-core`, `scex-util`, `scex-test`, `scex-java-test`) + .aggregate(`scex-macros`, `scex-core`, `scex-util`, `scex-test`) .settings(noPublishSettings: _*) lazy val `scex-macros` = project @@ -141,21 +139,6 @@ lazy val `scex-util` = project.dependsOn(`scex-core` % CompileAndTest) ) ) -lazy val `scex-test` = project.dependsOn(`scex-core`, `scex-util`) +lazy val `scex-test` = project.dependsOn(`scex-core` % CompileAndTest, `scex-util`) .settings(subprojectSettings: _*) - .settings(noPublishSettings: _*) - .settings( - libraryDependencies ++= Seq( - "com.vaadin" % "vaadin" % vaadinVersion, - "org.eclipse.jetty" % "jetty-server" % jettyVersion, - "org.eclipse.jetty" % "jetty-servlet" % jettyVersion, - "ch.qos.logback" % "logback-classic" % logbackVersion, - ) - ) - -lazy val `scex-java-test` = project.dependsOn(`scex-core`, `scex-util`) - .settings(subprojectSettings: _*) - .settings(noPublishSettings: _*) - .settings( - compileOrder := CompileOrder.JavaThenScala - ) + .settings(noPublishSettings: _*) \ No newline at end of file diff --git a/scex-java-test/src/main/java/JavaCostam.java b/scex-java-test/src/main/java/JavaCostam.java deleted file mode 100644 index cd670417..00000000 --- a/scex-java-test/src/main/java/JavaCostam.java +++ /dev/null @@ -1,11 +0,0 @@ -public class JavaCostam { - private final T fuu; - - public JavaCostam(T fuu) { - this.fuu = fuu; - } - - public T getFuu() { - return fuu; - } -} diff --git a/scex-java-test/src/main/java/ScexJavaTest.java b/scex-java-test/src/main/java/ScexJavaTest.java deleted file mode 100644 index ff3b4d4b..00000000 --- a/scex-java-test/src/main/java/ScexJavaTest.java +++ /dev/null @@ -1,69 +0,0 @@ -import com.avsystem.scex.Expression; -import com.avsystem.scex.ExpressionContext; -import com.avsystem.scex.ExpressionProfile; -import com.avsystem.scex.NamedSource; -import com.avsystem.scex.compiler.ScexSettings; -import com.avsystem.scex.compiler.presentation.ScexPresentationCompiler; -import com.avsystem.scex.japi.DefaultJavaScexCompiler; -import com.avsystem.scex.presentation.Attributes; -import com.avsystem.scex.presentation.SymbolAttributes; -import com.avsystem.scex.symboldsl.SymbolInfo; -import com.avsystem.scex.util.SimpleContext; -import com.avsystem.scex.validation.SymbolValidator; -import com.avsystem.scex.validation.SyntaxValidator; -import com.google.common.reflect.TypeToken; -import scala.collection.JavaConverters; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; - -@SuppressWarnings("deprecation") -public class ScexJavaTest { - public static void main(String[] args) throws Exception { - DefaultJavaScexCompiler compiler = new DefaultJavaScexCompiler(new ScexSettings()); - - SyntaxValidator syntaxValidator = compiler.compileSyntaxValidator(NamedSource.apply("test", readResource("/syntaxValidator.scala"))); - SymbolValidator symbolValidator = compiler.compileSymbolValidator(NamedSource.apply("test", readResource("/symbolValidator.scala"))); - SymbolAttributes symbolAttributes = compiler.compileSymbolAttributes(NamedSource.apply("test", readResource("/symbolAttributes.scala"))); - - for (SymbolInfo info : JavaConverters.seqAsJavaList(symbolAttributes.infoList())) { - System.out.println(info); - } - - Expression, Void> expr = - compiler.buildExpression().contextType(new TypeToken>() { - }).resultType(Void.class).get(); - - ExpressionProfile profile = new ExpressionProfile("test", syntaxValidator, symbolValidator, - symbolAttributes, "", NamedSource.apply("test", "")); - - Class> aecClass = (Class) ExpressionContext.class; - ScexPresentationCompiler.Completer ctx = compiler.buildCompleter().contextType(aecClass) - .resultType(String.class).profile(profile).template(false).get(); - - System.out.println("FUUUUUUU"); - - long start = System.currentTimeMillis(); - for (int i = 0; i < 3000; i++) { - ctx.getTypeCompletion("new JavaCostam(\"asdfasdaasd\").toSrslyWtf", 10); - if (i % 100 == 0) { - System.out.println(i); - } - } - System.out.println("FINISHED"); - long duration = System.currentTimeMillis() - start; - System.out.println(3000000.0 / duration); - } - - private static String readResource(String resource) throws IOException { - BufferedReader rd = new BufferedReader(new InputStreamReader(ScexJavaTest.class.getResourceAsStream(resource))); - StringBuilder sb = new StringBuilder(); - String line; - while ((line = rd.readLine()) != null) { - sb.append(line); - sb.append("\n"); - } - return sb.toString(); - } -} diff --git a/scex-java-test/src/main/resources/symbolAttributes.scala b/scex-java-test/src/main/resources/symbolAttributes.scala deleted file mode 100644 index 18c3d9c1..00000000 --- a/scex-java-test/src/main/resources/symbolAttributes.scala +++ /dev/null @@ -1,11 +0,0 @@ -import com.avsystem.scex.presentation.Attributes -import com.avsystem.scex.presentation.SymbolAttributes._ - -attributes { - on { s: String => - s.charAt _ --> Attributes( - paramNames = List("index"), - documentation = "Returns character at specified index of the string (ranging from 0 to length-1)".stripMargin - ) - } -} diff --git a/scex-java-test/src/main/resources/symbolValidator.scala b/scex-java-test/src/main/resources/symbolValidator.scala deleted file mode 100644 index 96f93695..00000000 --- a/scex-java-test/src/main/resources/symbolValidator.scala +++ /dev/null @@ -1,62 +0,0 @@ -import java.{lang => jl, util => ju} - -import com.avsystem.scex.validation.SymbolValidator._ - -allow { - StringContext.apply _ - String.CASE_INSENSITIVE_ORDER - Some.apply _ - String.valueOf(_: Boolean) - - on { c: JavaCostam[_] => - new JavaCostam(_: Nothing) - c.getFuu - c.toString - } - - on { s: String => - s.all.constructors - s.length - s.concat _ - s.matches _ - s.reverse - s.compare(_: String) - s.substring(_: Int) - } - - on { sc: StringContext => - sc.s _ - } - - on { al: ju.ArrayList[_] => - al.constructorWithSignature("(x$1: java.util.Collection[_ <: E])java.util.ArrayList[E]") - } - - on { any: Any => - any + (_: String) - any -> (_: Any) - any == (_: Any) - any != (_: Any) - } - - on { i: Int => - i.all.membersNamed("+") - } - -} ++ deny { - - on { any: Any => - any.equals _ - any.hashCode - any.## - any.getClass - any.asInstanceOf - any.isInstanceOf - } - - on { anyRef: AnyRef => - anyRef.eq _ - anyRef.synchronized _ - } - -} \ No newline at end of file diff --git a/scex-java-test/src/main/resources/syntaxValidator.scala b/scex-java-test/src/main/resources/syntaxValidator.scala deleted file mode 100644 index 428700f5..00000000 --- a/scex-java-test/src/main/resources/syntaxValidator.scala +++ /dev/null @@ -1,17 +0,0 @@ -import scala.reflect.macros.Universe - -def validateSyntax(u: Universe)(tree: u.Tree): (Boolean, List[u.Tree]) = { - import u._ - - def isLambdaParamDef(valDef: ValDef) = - valDef.mods.hasFlag(Flag.PARAM) && valDef.rhs == EmptyTree - - tree match { - case _: Block | _: Select | _: Apply | _: TypeApply | _: Ident | - _: If | _: Literal | _: New | _: This | _: Typed | _: TypTree => - (true, tree.children) - case Function(valDefs, body) if valDefs.forall(isLambdaParamDef) => - (true, body :: valDefs.map(_.tpt)) - case _ => (false, tree.children) - } -} diff --git a/scex-test/src/main/scala/vaadin/CompletionPlayground.scala b/scex-test/src/main/scala/vaadin/CompletionPlayground.scala deleted file mode 100644 index ba87e42c..00000000 --- a/scex-test/src/main/scala/vaadin/CompletionPlayground.scala +++ /dev/null @@ -1,141 +0,0 @@ -package vaadin - -import com.avsystem.commons.misc.TypeString -import com.avsystem.scex.compiler.ScexSettings -import com.avsystem.scex.compiler.presentation.ScexPresentationCompiler.Member -import com.avsystem.scex.japi.XmlFriendlyJavaScexCompiler -import com.avsystem.scex.presentation.SymbolAttributes -import com.avsystem.scex.util.PredefinedAccessSpecs.basicOperations -import com.avsystem.scex.util.SimpleContext -import com.avsystem.scex.validation.{SymbolValidator, SyntaxValidator} -import com.avsystem.scex.{ExpressionProfile, NamedSource} -import com.vaadin.event.FieldEvents.{TextChangeEvent, TextChangeListener} -import com.vaadin.terminal.gwt.server.AbstractApplicationServlet -import com.vaadin.ui.{Label, TextField, Window} - -import javax.servlet.http.HttpServletRequest -import org.eclipse.jetty.server.Server -import org.eclipse.jetty.server.session.SessionHandler -import org.eclipse.jetty.servlet.{ServletContextHandler, ServletHolder} - -import scala.annotation.nowarn - -@nowarn("msg=a pure expression does nothing in statement position") -object CompletionPlayground { - - val settings = new ScexSettings - - lazy val compiler = new XmlFriendlyJavaScexCompiler(settings) - - class SampleServlet extends AbstractApplicationServlet { - def getNewApplication(request: HttpServletRequest) = new SampleApplication - - def getApplicationClass = classOf[SampleApplication] - } - - class SampleApplication extends com.vaadin.Application { - def init(): Unit = try { - val window = new Window - setMainWindow(window) - - val acl = { - import com.avsystem.scex.validation.SymbolValidator._ - basicOperations ++ allow { - on { dyn: Dyn => - dyn.all.members - } - - on { inter: Intermediate => - inter.all.members - } - - on { interDyn: InterDyn => - interDyn.all.members - } - - on { jl: JavaLol => - jl.all.members - } - - on { l: List[Int] => - l.filter _ - l.all.membersNamed.map - } - - on { a: Array[_] => - a.as[Array[Any]].exists(_: Any => Boolean) - } - - on { r: JavaRoot => - r.all.members - } - - on { r: Root => - r.all.members - r.dyn - } - } - } - - val header = - """ - |import com.avsystem.scex.util.TypesafeEquals._ - """.stripMargin - - val utils = - """ - |val utilStuff = "dafuq" - """.stripMargin - - val profile = new ExpressionProfile("test", SyntaxValidator.SimpleExpressions, SymbolValidator(acl), - SymbolAttributes(Nil), header, NamedSource("test", utils)) - - def memberRepr(member: Member) = - s"${member.name}${member.params.map(_.map(p => s"${p.name}: ${p.tpe}-${p.tpe.erasure}").mkString("(", ", ", ")")).mkString}: " + - s"${member.returnType}-${member.returnType.erasure} - ${member.documentation}" - - val completer = compiler.getCompleter[SimpleContext[Root], String](profile, variableTypes = - Map("someInt" -> TypeString[Int], "intList" -> TypeString[List[Int]])) - val scopeMembers = completer.getScopeCompletion.members.filterNot(_.flags.iimplicit).map(memberRepr).mkString("\n") - - val textField = new TextField - textField.setWidth("100%") - - val label = new Label - label.setContentMode(Label.CONTENT_PREFORMATTED) - - textField.addListener(new TextChangeListener { - def textChange(event: TextChangeEvent): Unit = { - val pos = event.getCursorPosition - 1 - val completion = completer.getTypeCompletion(event.getText + "}}}", pos) - val errors = completer.getErrors(event.getText).mkString("\n") - val members = completion.members.filterNot(_.flags.iimplicit).map(memberRepr).mkString("\n") - val parsedTree = completer.parse(event.getText) - val typedPrefix = completion.typedPrefixTree - val parsedPrefix = parsedTree.locate(typedPrefix.attachments.position) - label.setValue(s"POSITION: $pos\nPARSED:\n${parsedTree.pretty(withPositions = true, withTypes = true)}\nERRORS:\n$errors\nPPREFIX:\n${parsedPrefix.pretty(withPositions = true, withTypes = true)}\n" + - s"TPREFIX:\n${typedPrefix.pretty(withPositions = true, withTypes = true)}\nCOMPLETION:\n$members\nSCOPE COMPLETION:\n$scopeMembers") - } - }) - - window.addComponent(textField) - window.addComponent(label) - } catch { - case e: Exception => - e.printStackTrace() - } - } - - def main(args: Array[String]): Unit = { - val server = new Server(9090) - - val handler = new ServletContextHandler - handler.addServlet(new ServletHolder(new SampleServlet), "/*") - handler.setSessionHandler(new SessionHandler) - - server.setHandler(handler) - - server.start() - server.join() - } -} diff --git a/scex-test/src/main/scala/vaadin/Dyn.scala b/scex-test/src/main/scala/vaadin/Dyn.scala deleted file mode 100644 index 3121fe4a..00000000 --- a/scex-test/src/main/scala/vaadin/Dyn.scala +++ /dev/null @@ -1,27 +0,0 @@ -package vaadin - -import scala.language.dynamics - -/** - * Created: 10-12-2013 - * Author: ghik - */ -trait Dyn extends Dynamic { - def selectDynamic(name: String): String - - def intermediate: Intermediate - - def intermediateMeth(str: Int): Intermediate - - def interDyn: InterDyn -} - -trait InterDyn extends Dynamic { - def selectDynamic(name: String): Intermediate - - def inter: Intermediate -} - -trait Intermediate { - def moreDyn: Dyn -} diff --git a/scex-test/src/main/scala/vaadin/JavaLol.java b/scex-test/src/main/scala/vaadin/JavaLol.java deleted file mode 100644 index 6e78dae4..00000000 --- a/scex-test/src/main/scala/vaadin/JavaLol.java +++ /dev/null @@ -1,31 +0,0 @@ -package vaadin; - -public class JavaLol { - public static class StaticLol { - - } - - public class InnerLol { - - } - - public int fuu = 5; - - private int lol; - - public int getLol() { - return lol; - } - - public void setLol(int lol) { - this.lol = lol; - } - - public boolean isFoo() { - return true; - } - - public String format() { - return ""; - } -} diff --git a/scex-test/src/main/scala/vaadin/JavaRoot.java b/scex-test/src/main/scala/vaadin/JavaRoot.java deleted file mode 100644 index f1eadb9e..00000000 --- a/scex-test/src/main/scala/vaadin/JavaRoot.java +++ /dev/null @@ -1,19 +0,0 @@ -package vaadin; - -/** - * Author: ghik - * Created: 13/01/15. - */ -public class JavaRoot { - public int LOLZSY = 5; - - private final String name = "NAME"; - - public String getSzjet() { - return "szjet"; - } - - public String getName() { - return name; - } -} diff --git a/scex-test/src/main/scala/vaadin/Root.scala b/scex-test/src/main/scala/vaadin/Root.scala deleted file mode 100644 index 43b585c5..00000000 --- a/scex-test/src/main/scala/vaadin/Root.scala +++ /dev/null @@ -1,25 +0,0 @@ -package vaadin - -/** - * Created: 11-12-2013 - * Author: ghik - */ -trait Root extends JavaRoot { - def stuff = 5 - - def javaLol = new JavaLol - - val tehList = List(1, 2, 3) - - def goodyn: Dyn - - def format: String -} - -object Root { - - implicit class Costam(root: Root) { - def dyn: Dyn = ??? - } - -} From 23f17bc67481f2bf2d7e26da5c752f0da461ee47 Mon Sep 17 00:00:00 2001 From: Dawid Dworak Date: Mon, 8 Jul 2024 13:07:22 +0200 Subject: [PATCH 02/14] Remove joda-time --- build.sbt | 2 -- .../scex/util/CommonSymbolValidators.scala | 4 --- .../com/avsystem/scex/util/EnrichedDate.scala | 31 ++++++++++--------- .../scex/util/function/FormatUtilImpl.java | 30 +++++------------- 4 files changed, 23 insertions(+), 44 deletions(-) diff --git a/build.sbt b/build.sbt index de155762..1bdebfdd 100644 --- a/build.sbt +++ b/build.sbt @@ -29,7 +29,6 @@ val commonsLang3Version = "3.14.0" val commonsCodecVersion = "1.17.0" val guavaVersion = "33.2.1-jre" val commonsNetVersion = "3.11.1" -val jodaTimeVersion = "2.12.7" val scalatestVersion = "3.2.19" val noPublishSettings = Seq( @@ -135,7 +134,6 @@ lazy val `scex-util` = project.dependsOn(`scex-core` % CompileAndTest) libraryDependencies ++= Seq( "org.apache.commons" % "commons-lang3" % commonsLang3Version, "commons-net" % "commons-net" % commonsNetVersion, - "joda-time" % "joda-time" % jodaTimeVersion ) ) diff --git a/scex-util/src/main/scala/com/avsystem/scex/util/CommonSymbolValidators.scala b/scex-util/src/main/scala/com/avsystem/scex/util/CommonSymbolValidators.scala index 836df424..61fea0f6 100644 --- a/scex-util/src/main/scala/com/avsystem/scex/util/CommonSymbolValidators.scala +++ b/scex-util/src/main/scala/com/avsystem/scex/util/CommonSymbolValidators.scala @@ -2,7 +2,6 @@ package com.avsystem.scex.util import com.avsystem.commons.jiop.JavaInterop._ import com.avsystem.scex.util.function._ -import org.joda.time.Hours import scala.annotation.nowarn import scala.util.matching.Regex.Match @@ -132,9 +131,6 @@ object CommonSymbolValidators { dateSplicer.toString(_: JDate) collectionSplicer.toString(_: JSet[String]) - allStatic[Hours].members - on { h: Hours => h.all.introduced.members } - on { so: StringNetworkOps => so.all.introduced.members } on { so: StringMiscOps => so.all.introduced.members } on { ed: EnrichedDate => ed.all.introduced.members } diff --git a/scex-util/src/main/scala/com/avsystem/scex/util/EnrichedDate.scala b/scex-util/src/main/scala/com/avsystem/scex/util/EnrichedDate.scala index ef739af8..9ce544d0 100644 --- a/scex-util/src/main/scala/com/avsystem/scex/util/EnrichedDate.scala +++ b/scex-util/src/main/scala/com/avsystem/scex/util/EnrichedDate.scala @@ -1,14 +1,15 @@ package com.avsystem.scex.util -import java.text.SimpleDateFormat -import java.util.{Calendar, Date} - import com.avsystem.scex.presentation.annotation.Documentation import org.apache.commons.lang3.time.DateUtils -import org.joda.time.DateTime + +import java.text.SimpleDateFormat +import java.time.Instant +import java.time.temporal.ChronoField +import java.util.{Calendar, Date} final class EnrichedDate(private val wrapped: Date) extends AnyVal { - private def dateTime = new DateTime(wrapped.getTime) + private def instant = Instant.ofEpochMilli(wrapped.getTime) @Documentation("Formats the date using the default date format: yyyy.MM.dd HH:mm:ss.") def format: String = CommonDateFormat.get.format(wrapped) @@ -49,27 +50,27 @@ final class EnrichedDate(private val wrapped: Date) extends AnyVal { def millis: Long = wrapped.getTime @Documentation("Returns the seconds field of the date expressed in milliseconds.") - def millisOfSecond: Int = dateTime.getMillisOfSecond + def millisOfSecond: Int = instant.get(ChronoField.MILLI_OF_SECOND) @Documentation("Returns the seconds field of the date.") - def secondOfMinute: Int = dateTime.getSecondOfMinute + def secondOfMinute: Int = instant.get(ChronoField.SECOND_OF_MINUTE) @Documentation("Returns the number of seconds which passed since 00:00 for the date.") - def secondOfDay: Int = dateTime.getSecondOfDay + def secondOfDay: Int = instant.get(ChronoField.SECOND_OF_DAY) @Documentation("Returns the minutes field of the date.") - def minuteOfHour: Int = dateTime.getMinuteOfHour + def minuteOfHour: Int = instant.get(ChronoField.MINUTE_OF_HOUR) @Documentation("Returns the number of minutes which passed since 00:00 for the date.") - def minuteOfDay: Int = dateTime.getMinuteOfDay + def minuteOfDay: Int = instant.get(ChronoField.MINUTE_OF_DAY) @Documentation("Returns the number of hours which passed since 00:00 for the date.") - def hourOfDay: Int = dateTime.getHourOfDay + def hourOfDay: Int = instant.get(ChronoField.HOUR_OF_DAY) @Documentation("Returns the day field of the date.") - def dayOfMonth: Int = dateTime.getDayOfMonth + def dayOfMonth: Int = instant.get(ChronoField.DAY_OF_MONTH) @Documentation("Returns a numeric value for day of the week of the date, where 1 - Monday, 7 - Sunday.") - def dayOfWeek: Int = dateTime.getDayOfWeek + def dayOfWeek: Int = instant.get(ChronoField.DAY_OF_WEEK) @Documentation("Returns a numeric value for the day of the year.") - def dayOfYear: Int = dateTime.getDayOfYear + def dayOfYear: Int = instant.get(ChronoField.DAY_OF_YEAR) @Documentation("Returns a numeric value for the month of the year, where 1 - January, 12 - December.") - def monthOfYear: Int = dateTime.getMonthOfYear + def monthOfYear: Int = instant.get(ChronoField.MONTH_OF_YEAR) } diff --git a/scex-util/src/main/scala/com/avsystem/scex/util/function/FormatUtilImpl.java b/scex-util/src/main/scala/com/avsystem/scex/util/function/FormatUtilImpl.java index a78bb979..71e22983 100644 --- a/scex-util/src/main/scala/com/avsystem/scex/util/function/FormatUtilImpl.java +++ b/scex-util/src/main/scala/com/avsystem/scex/util/function/FormatUtilImpl.java @@ -1,16 +1,14 @@ package com.avsystem.scex.util.function; import org.apache.commons.lang3.StringUtils; -import org.joda.time.Period; -import org.joda.time.PeriodType; -import org.joda.time.format.PeriodFormatter; -import org.joda.time.format.PeriodFormatterBuilder; +import org.apache.commons.lang3.time.DurationFormatUtils; import java.math.BigDecimal; import java.math.MathContext; +import java.time.Duration; +import java.time.Instant; import java.util.Collection; import java.util.Date; -import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -22,25 +20,12 @@ public class FormatUtilImpl implements FormatUtil { private static final Pattern numericPattern = Pattern.compile(NUMERIC_PATTERN); public static String getPeriodMessage(Date from) { - Period period = new Period(from.getTime(), new Date().getTime()); - String periodMessage = INSTANCE.formatPeriod(period); - return StringUtils.substringBefore(periodMessage, " "); + return StringUtils.substringBefore(formatDuration(Duration.between(from.toInstant(), Instant.now())), " "); } private FormatUtilImpl() { } - private static final PeriodFormatter PERIOD_FORMATTER = new PeriodFormatterBuilder() - .printZeroNever() - .appendYears().appendSuffix("y ") - .appendMonths().appendSuffix("M ") - .appendDays().appendSuffix("d ") - .appendHours().appendSuffix("h ") - .printZeroAlways() - .appendMinutes().appendSuffix("m ") - .appendSeconds().appendSuffix("s ") - .toFormatter(); - private static final String[] Q = new String[] { "", "k", "M", "G", "T", "P", "E" }; private Long SI = 1000L; private Long BINARY = 1024L; @@ -80,14 +65,13 @@ public String normalizeBytesWithDot(long bytes) { return normalizeBytes(bytes).replace(",", "."); } - @Override public String secondsToPeriod(long seconds) { - return formatPeriod(new Period(TimeUnit.SECONDS.toMillis(seconds))); + return formatDuration(Duration.ofSeconds(seconds)); } - public String formatPeriod(Period period) { - return PERIOD_FORMATTER.print(period.normalizedStandard(PeriodType.yearMonthDayTime())); + public static String formatDuration(Duration duration) { + return DurationFormatUtils.formatDuration(duration.toMillis(), "[d'd 'H'h 'm'm ']s's'"); } /** From 9cba44e04921c73287f19127f17eed14d000ed9d Mon Sep 17 00:00:00 2001 From: Dawid Dworak Date: Mon, 8 Jul 2024 13:12:18 +0200 Subject: [PATCH 03/14] Update github actions --- .github/workflows/ci.yml | 8 +++++++- project/plugins.sbt | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 48528287..541cc47e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,6 +48,9 @@ jobs: java-version: 21 cache: sbt + - name: Setup sbt + uses: sbt/setup-sbt@v1 + - name: Check that workflows are up to date run: sbt '++ ${{ matrix.scala }}' githubWorkflowCheck @@ -55,7 +58,7 @@ jobs: run: sbt '++ ${{ matrix.scala }}' test - name: Compress target directories - run: tar cf targets.tar scex-macros/target scex-core/target scex-test/target scex-java-test/target scex-util/target target project/target + run: tar cf targets.tar scex-macros/target scex-core/target scex-test/target scex-util/target target project/target - name: Upload target directories uses: actions/upload-artifact@v4 @@ -95,6 +98,9 @@ jobs: java-version: 21 cache: sbt + - name: Setup sbt + uses: sbt/setup-sbt@v1 + - name: Download target directories (2.13.14) uses: actions/download-artifact@v4 with: diff --git a/project/plugins.sbt b/project/plugins.sbt index ec61a1f6..55bbebc6 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -2,4 +2,4 @@ logLevel := Level.Warn addSbtPlugin("org.jetbrains.scala" % "sbt-ide-settings" % "1.1.2") addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.5.12") -addSbtPlugin("com.github.sbt" % "sbt-github-actions" % "0.23.0") +addSbtPlugin("com.github.sbt" % "sbt-github-actions" % "0.24.0") \ No newline at end of file From 5f19d2b6228921bfb1f7e586c82f80a06871b26a Mon Sep 17 00:00:00 2001 From: Dawid Dworak Date: Wed, 14 Aug 2024 15:26:07 +0200 Subject: [PATCH 04/14] Remove redundant FormatUtilImpl.getPeriodMessage --- .../com/avsystem/scex/util/function/FormatUtilImpl.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/scex-util/src/main/scala/com/avsystem/scex/util/function/FormatUtilImpl.java b/scex-util/src/main/scala/com/avsystem/scex/util/function/FormatUtilImpl.java index 71e22983..2ecfb0fb 100644 --- a/scex-util/src/main/scala/com/avsystem/scex/util/function/FormatUtilImpl.java +++ b/scex-util/src/main/scala/com/avsystem/scex/util/function/FormatUtilImpl.java @@ -6,9 +6,7 @@ import java.math.BigDecimal; import java.math.MathContext; import java.time.Duration; -import java.time.Instant; import java.util.Collection; -import java.util.Date; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -19,10 +17,6 @@ public class FormatUtilImpl implements FormatUtil { private static final Pattern numericPattern = Pattern.compile(NUMERIC_PATTERN); - public static String getPeriodMessage(Date from) { - return StringUtils.substringBefore(formatDuration(Duration.between(from.toInstant(), Instant.now())), " "); - } - private FormatUtilImpl() { } From 6d3c6712d8e7499d6b1ad5a870aa068505501422 Mon Sep 17 00:00:00 2001 From: Dawid Dworak Date: Wed, 14 Aug 2024 15:26:15 +0200 Subject: [PATCH 05/14] Add EnrichedDateTest --- .../avsystem/scex/util/EnrichedDateTest.scala | 111 ++++++++++++++++-- 1 file changed, 103 insertions(+), 8 deletions(-) diff --git a/scex-util/src/test/scala/com/avsystem/scex/util/EnrichedDateTest.scala b/scex-util/src/test/scala/com/avsystem/scex/util/EnrichedDateTest.scala index ac529388..ef2eeee3 100644 --- a/scex-util/src/test/scala/com/avsystem/scex/util/EnrichedDateTest.scala +++ b/scex-util/src/test/scala/com/avsystem/scex/util/EnrichedDateTest.scala @@ -1,12 +1,13 @@ package com.avsystem.scex.util import java.util.{Calendar, Date} - import org.scalatest.funsuite.AnyFunSuite +import java.time.ZoneId + class EnrichedDateTest extends AnyFunSuite { // 2019-04-17 12:55:14.456 UTC - val testDate: EnrichedDate = new EnrichedDate(new Date(1555505714456L)) + val testDate: EnrichedDate = new EnrichedDate(new Date(1555505714456L), ZoneId.of("Europe/Warsaw")) def localMidnight: Calendar = { val time = Calendar.getInstance() @@ -17,28 +18,122 @@ class EnrichedDateTest extends AnyFunSuite { time } + test("format with default format") { + assert(testDate.format == "2019.04.17 14:55:14") + } + + test("format with custom format") { + assert(testDate.format("yyyy-MM-dd") == "2019-04-17") + } + + test("addMilliseconds") { + assert(testDate.addMilliseconds(1000) == new Date(1555505715456L)) + } + + test("addSeconds") { + assert(testDate.addSeconds(1) == new Date(1555505715456L)) + } + + test("addMinutes") { + assert(testDate.addMinutes(1) == new Date(1555505774456L)) + } + + test("addHours") { + assert(testDate.addHours(1) == new Date(1555509314456L)) + } + + test("addDays") { + assert(testDate.addDays(1) == new Date(1555592114456L)) + } + + test("addWeeks") { + assert(testDate.addWeeks(1) == new Date(1556110514456L)) + } + + test("addMonths") { + assert(testDate.addMonths(1) == new Date(1558097714456L)) + } + // 2019-04-17 12:55:14.000 UTC - test("truncate to seconds")(assert(testDate.truncateToSeconds == new Date(1555505714000L))) + test("truncate to seconds") { + assert(testDate.truncateToSeconds == new Date(1555505714000L)) + } // 2019-04-17 12:55:00.000 UTC - test("truncate to minutes")(assert(testDate.truncateToMinutes == new Date(1555505700000L))) + test("truncate to minutes") { + assert(testDate.truncateToMinutes == new Date(1555505700000L)) + } // 2019-04-17 12:00:00.000 UTC - test("truncate to hours")(assert(testDate.truncateToHours == new Date(1555502400000L))) + test("truncate to hours") { + assert(testDate.truncateToHours == new Date(1555502400000L)) + } // 2019-04-17 00:00:00.000 default time zone + val truncatedToDays: Calendar = localMidnight truncatedToDays.set(Calendar.YEAR, 2019) truncatedToDays.set(Calendar.MONTH, Calendar.APRIL) truncatedToDays.set(Calendar.DAY_OF_MONTH, 17) - test("truncate to day")(assert(testDate.truncateToDays == truncatedToDays.getTime)) + test("truncate to day") { + assert(testDate.truncateToDays == truncatedToDays.getTime) + } // 2019-04-01 00:00:00.000 default time zone val truncatedToMonths: Calendar = localMidnight truncatedToMonths.set(Calendar.YEAR, 2019) truncatedToMonths.set(Calendar.MONTH, Calendar.APRIL) truncatedToMonths.set(Calendar.DAY_OF_MONTH, 1) - test("truncate to month")(assert(testDate.truncateToMonths == truncatedToMonths.getTime)) + test("truncate to month") { + assert(testDate.truncateToMonths == truncatedToMonths.getTime) + } // 2019-01-01 00:00:00.000 default time zone val truncatedToYears: Calendar = localMidnight truncatedToYears.set(Calendar.YEAR, 2019) truncatedToYears.set(Calendar.MONTH, Calendar.JANUARY) truncatedToYears.set(Calendar.DAY_OF_MONTH, 1) - test("truncate to year")(assert(testDate.truncateToYears == truncatedToYears.getTime)) + test("truncate to year") { + assert(testDate.truncateToYears == truncatedToYears.getTime) + } + + test("millis") { + assert(testDate.millis == 1555505714456L) + } + + test("millisOfSecond") { + assert(testDate.millisOfSecond == 456) + } + + test("secondOfMinute") { + assert(testDate.secondOfMinute == 14) + } + + test("secondOfDay") { + assert(testDate.secondOfDay == 53714) + } + + test("minuteOfHour") { + assert(testDate.minuteOfHour == 55) + } + + test("minuteOfDay") { + assert(testDate.minuteOfDay == 895) + } + + test("hourOfDay") { + assert(testDate.hourOfDay == 14) + } + + test("dayOfMonth") { + assert(testDate.dayOfMonth == 17) + } + + test("dayOfWeek") { + assert(testDate.dayOfWeek == 3) + } + + test("dayOfYear") { + assert(testDate.dayOfYear == 107) + } + + test("monthOfYear") { + assert(testDate.monthOfYear == 4) + } + } From f19898610a43ae928c70499c615b382ae730c0fc Mon Sep 17 00:00:00 2001 From: Dawid Dworak Date: Wed, 14 Aug 2024 15:26:30 +0200 Subject: [PATCH 06/14] Fix EnrichedDate using ZoneDateTime --- .../com/avsystem/scex/util/EnrichedDate.scala | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/scex-util/src/main/scala/com/avsystem/scex/util/EnrichedDate.scala b/scex-util/src/main/scala/com/avsystem/scex/util/EnrichedDate.scala index 9ce544d0..567db187 100644 --- a/scex-util/src/main/scala/com/avsystem/scex/util/EnrichedDate.scala +++ b/scex-util/src/main/scala/com/avsystem/scex/util/EnrichedDate.scala @@ -4,12 +4,12 @@ import com.avsystem.scex.presentation.annotation.Documentation import org.apache.commons.lang3.time.DateUtils import java.text.SimpleDateFormat -import java.time.Instant import java.time.temporal.ChronoField +import java.time.{Clock, Instant, ZoneId, ZonedDateTime} import java.util.{Calendar, Date} -final class EnrichedDate(private val wrapped: Date) extends AnyVal { - private def instant = Instant.ofEpochMilli(wrapped.getTime) +final class EnrichedDate(wrapped: Date, zone: ZoneId = Clock.systemDefaultZone.getZone) { + private def zonedDateTime: ZonedDateTime = ZonedDateTime.ofInstant(Instant.ofEpochMilli(wrapped.getTime), zone) @Documentation("Formats the date using the default date format: yyyy.MM.dd HH:mm:ss.") def format: String = CommonDateFormat.get.format(wrapped) @@ -50,27 +50,27 @@ final class EnrichedDate(private val wrapped: Date) extends AnyVal { def millis: Long = wrapped.getTime @Documentation("Returns the seconds field of the date expressed in milliseconds.") - def millisOfSecond: Int = instant.get(ChronoField.MILLI_OF_SECOND) + def millisOfSecond: Int = zonedDateTime.get(ChronoField.MILLI_OF_SECOND) @Documentation("Returns the seconds field of the date.") - def secondOfMinute: Int = instant.get(ChronoField.SECOND_OF_MINUTE) + def secondOfMinute: Int = zonedDateTime.get(ChronoField.SECOND_OF_MINUTE) @Documentation("Returns the number of seconds which passed since 00:00 for the date.") - def secondOfDay: Int = instant.get(ChronoField.SECOND_OF_DAY) + def secondOfDay: Int = zonedDateTime.get(ChronoField.SECOND_OF_DAY) @Documentation("Returns the minutes field of the date.") - def minuteOfHour: Int = instant.get(ChronoField.MINUTE_OF_HOUR) + def minuteOfHour: Int = zonedDateTime.get(ChronoField.MINUTE_OF_HOUR) @Documentation("Returns the number of minutes which passed since 00:00 for the date.") - def minuteOfDay: Int = instant.get(ChronoField.MINUTE_OF_DAY) + def minuteOfDay: Int = zonedDateTime.get(ChronoField.MINUTE_OF_DAY) @Documentation("Returns the number of hours which passed since 00:00 for the date.") - def hourOfDay: Int = instant.get(ChronoField.HOUR_OF_DAY) + def hourOfDay: Int = zonedDateTime.get(ChronoField.HOUR_OF_DAY) @Documentation("Returns the day field of the date.") - def dayOfMonth: Int = instant.get(ChronoField.DAY_OF_MONTH) + def dayOfMonth: Int = zonedDateTime.get(ChronoField.DAY_OF_MONTH) @Documentation("Returns a numeric value for day of the week of the date, where 1 - Monday, 7 - Sunday.") - def dayOfWeek: Int = instant.get(ChronoField.DAY_OF_WEEK) + def dayOfWeek: Int = zonedDateTime.get(ChronoField.DAY_OF_WEEK) @Documentation("Returns a numeric value for the day of the year.") - def dayOfYear: Int = instant.get(ChronoField.DAY_OF_YEAR) + def dayOfYear: Int = zonedDateTime.get(ChronoField.DAY_OF_YEAR) @Documentation("Returns a numeric value for the month of the year, where 1 - January, 12 - December.") - def monthOfYear: Int = instant.get(ChronoField.MONTH_OF_YEAR) + def monthOfYear: Int = zonedDateTime.get(ChronoField.MONTH_OF_YEAR) } From 29e2b8000c4ef859ef8b574f2682887e6f0429de Mon Sep 17 00:00:00 2001 From: Dawid Dworak Date: Wed, 14 Aug 2024 15:32:23 +0200 Subject: [PATCH 07/14] Update dependencies --- build.sbt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build.sbt b/build.sbt index 1bdebfdd..e5009a52 100644 --- a/build.sbt +++ b/build.sbt @@ -22,11 +22,11 @@ inThisBuild(Seq( val CompileAndTest = "compile->compile;test->test" val parserCombinatorsVersion = "2.4.0" -val avsCommonsVersion = "2.16.0" -val slf4jVersion = "2.0.13" +val avsCommonsVersion = "2.18.0" +val slf4jVersion = "2.0.16" val logbackVersion = "1.5.6" // Tests only -val commonsLang3Version = "3.14.0" -val commonsCodecVersion = "1.17.0" +val commonsLang3Version = "3.16.0" +val commonsCodecVersion = "1.17.1" val guavaVersion = "33.2.1-jre" val commonsNetVersion = "3.11.1" val scalatestVersion = "3.2.19" From 0ab53c18d98c8e8a4cd978ddb8b26aa9cdbc21a7 Mon Sep 17 00:00:00 2001 From: Dawid Dworak Date: Wed, 14 Aug 2024 15:41:10 +0200 Subject: [PATCH 08/14] Take zone into account for EnrichedDate.format --- .../src/main/scala/com/avsystem/scex/util/EnrichedDate.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scex-util/src/main/scala/com/avsystem/scex/util/EnrichedDate.scala b/scex-util/src/main/scala/com/avsystem/scex/util/EnrichedDate.scala index 567db187..19833ac2 100644 --- a/scex-util/src/main/scala/com/avsystem/scex/util/EnrichedDate.scala +++ b/scex-util/src/main/scala/com/avsystem/scex/util/EnrichedDate.scala @@ -3,7 +3,7 @@ package com.avsystem.scex.util import com.avsystem.scex.presentation.annotation.Documentation import org.apache.commons.lang3.time.DateUtils -import java.text.SimpleDateFormat +import java.time.format.DateTimeFormatter import java.time.temporal.ChronoField import java.time.{Clock, Instant, ZoneId, ZonedDateTime} import java.util.{Calendar, Date} @@ -15,7 +15,7 @@ final class EnrichedDate(wrapped: Date, zone: ZoneId = Clock.systemDefaultZone.g def format: String = CommonDateFormat.get.format(wrapped) @Documentation("Formats the date according to provided date format. An example of correct date format is yyyy.MM.dd HH:mm:ss.") - def format(dateFormat: String): String = new SimpleDateFormat(dateFormat).format(wrapped) + def format(dateFormat: String): String = DateTimeFormatter.ofPattern(dateFormat).format(zonedDateTime) @Documentation("Adds the provided amount of milliseconds to the date.") def addMilliseconds(amount: Int): Date = DateUtils.addMilliseconds(wrapped, amount) From db52d4b76c7c906fe743369fb48b0687b70bd4d7 Mon Sep 17 00:00:00 2001 From: Dawid Dworak Date: Wed, 14 Aug 2024 16:03:34 +0200 Subject: [PATCH 09/14] Fix argless EnrichedDate.format --- .../scala/com/avsystem/scex/util/CommonDateFormat.scala | 8 ++++---- .../main/scala/com/avsystem/scex/util/EnrichedDate.scala | 8 ++++++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/scex-util/src/main/scala/com/avsystem/scex/util/CommonDateFormat.scala b/scex-util/src/main/scala/com/avsystem/scex/util/CommonDateFormat.scala index 131d2539..40cb4344 100644 --- a/scex-util/src/main/scala/com/avsystem/scex/util/CommonDateFormat.scala +++ b/scex-util/src/main/scala/com/avsystem/scex/util/CommonDateFormat.scala @@ -1,15 +1,15 @@ package com.avsystem.scex.util -import java.text.{DateFormat, SimpleDateFormat} +import java.text.SimpleDateFormat object CommonDateFormat { - private val dateFormatTL = new ThreadLocal[DateFormat] { - override def initialValue(): DateFormat = { + private val dateFormatTL = new ThreadLocal[SimpleDateFormat] { + override def initialValue(): SimpleDateFormat = { val df = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss") df.setLenient(false) df } } - def get: DateFormat = dateFormatTL.get + def get: SimpleDateFormat = dateFormatTL.get } diff --git a/scex-util/src/main/scala/com/avsystem/scex/util/EnrichedDate.scala b/scex-util/src/main/scala/com/avsystem/scex/util/EnrichedDate.scala index 19833ac2..aa5fe52d 100644 --- a/scex-util/src/main/scala/com/avsystem/scex/util/EnrichedDate.scala +++ b/scex-util/src/main/scala/com/avsystem/scex/util/EnrichedDate.scala @@ -8,11 +8,15 @@ import java.time.temporal.ChronoField import java.time.{Clock, Instant, ZoneId, ZonedDateTime} import java.util.{Calendar, Date} -final class EnrichedDate(wrapped: Date, zone: ZoneId = Clock.systemDefaultZone.getZone) { +final class EnrichedDate(wrapped: Date, zone: ZoneId = Clock.systemDefaultZone().getZone) { private def zonedDateTime: ZonedDateTime = ZonedDateTime.ofInstant(Instant.ofEpochMilli(wrapped.getTime), zone) @Documentation("Formats the date using the default date format: yyyy.MM.dd HH:mm:ss.") - def format: String = CommonDateFormat.get.format(wrapped) + def format: String = + if (zone == Clock.systemDefaultZone().getZone) + CommonDateFormat.get.format(wrapped) + else + format(CommonDateFormat.get.toPattern) @Documentation("Formats the date according to provided date format. An example of correct date format is yyyy.MM.dd HH:mm:ss.") def format(dateFormat: String): String = DateTimeFormatter.ofPattern(dateFormat).format(zonedDateTime) From 4c95fb3f46a0da0abb4afa05a0c3cef64ab8d820 Mon Sep 17 00:00:00 2001 From: Dawid Dworak Date: Wed, 21 Aug 2024 14:54:39 +0200 Subject: [PATCH 10/14] Cosmetic --- .../main/scala/com/avsystem/scex/util/CommonDateFormat.scala | 2 ++ .../src/main/scala/com/avsystem/scex/util/EnrichedDate.scala | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/scex-util/src/main/scala/com/avsystem/scex/util/CommonDateFormat.scala b/scex-util/src/main/scala/com/avsystem/scex/util/CommonDateFormat.scala index 40cb4344..0e56fed7 100644 --- a/scex-util/src/main/scala/com/avsystem/scex/util/CommonDateFormat.scala +++ b/scex-util/src/main/scala/com/avsystem/scex/util/CommonDateFormat.scala @@ -2,7 +2,9 @@ package com.avsystem.scex.util import java.text.SimpleDateFormat +// performance optimization, see https://github.com/AVSystem/scex/pull/38#discussion_r1724465654 object CommonDateFormat { + // thread-local instance of SimpleDateFormat because it's not thread-safe private val dateFormatTL = new ThreadLocal[SimpleDateFormat] { override def initialValue(): SimpleDateFormat = { val df = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss") diff --git a/scex-util/src/main/scala/com/avsystem/scex/util/EnrichedDate.scala b/scex-util/src/main/scala/com/avsystem/scex/util/EnrichedDate.scala index aa5fe52d..92327380 100644 --- a/scex-util/src/main/scala/com/avsystem/scex/util/EnrichedDate.scala +++ b/scex-util/src/main/scala/com/avsystem/scex/util/EnrichedDate.scala @@ -5,11 +5,11 @@ import org.apache.commons.lang3.time.DateUtils import java.time.format.DateTimeFormatter import java.time.temporal.ChronoField -import java.time.{Clock, Instant, ZoneId, ZonedDateTime} +import java.time.{Clock, ZoneId, ZonedDateTime} import java.util.{Calendar, Date} final class EnrichedDate(wrapped: Date, zone: ZoneId = Clock.systemDefaultZone().getZone) { - private def zonedDateTime: ZonedDateTime = ZonedDateTime.ofInstant(Instant.ofEpochMilli(wrapped.getTime), zone) + private def zonedDateTime: ZonedDateTime = ZonedDateTime.ofInstant(wrapped.toInstant, zone) @Documentation("Formats the date using the default date format: yyyy.MM.dd HH:mm:ss.") def format: String = From ab4cc0ebbf936eefd540bf92b2bab3ba2bd143f6 Mon Sep 17 00:00:00 2001 From: Dawid Dworak Date: Wed, 21 Aug 2024 15:12:30 +0200 Subject: [PATCH 11/14] Split EnrichedDate and EnrichedZonedDate to allow value class usage --- .../scex/util/CommonExpressionUtils.scala | 11 +++--- .../scex/util/CommonSymbolValidators.scala | 1 + .../com/avsystem/scex/util/EnrichedDate.scala | 34 ++++++++++++------- .../avsystem/scex/util/EnrichedDateTest.scala | 8 +++-- 4 files changed, 35 insertions(+), 19 deletions(-) diff --git a/scex-util/src/main/scala/com/avsystem/scex/util/CommonExpressionUtils.scala b/scex-util/src/main/scala/com/avsystem/scex/util/CommonExpressionUtils.scala index d8327ce6..f84796f2 100644 --- a/scex-util/src/main/scala/com/avsystem/scex/util/CommonExpressionUtils.scala +++ b/scex-util/src/main/scala/com/avsystem/scex/util/CommonExpressionUtils.scala @@ -1,14 +1,14 @@ package com.avsystem.scex.util -import java.text.ParseException -import java.util.Date - import com.avsystem.commons._ import com.avsystem.scex.SetterConversion import com.avsystem.scex.compiler.TemplateInterpolations.Splicer import com.avsystem.scex.presentation.annotation.Documentation import com.avsystem.scex.util.function._ +import java.text.ParseException +import java.util.Date + /** * Author: ghik * Created: 16/10/15. @@ -46,6 +46,9 @@ object CommonExpressionUtils { implicit def enrichDate(date: JDate): EnrichedDate = new EnrichedDate(date) + implicit def enrichDateTime(date: JDate): EnrichedZonedDate = + EnrichedZonedDate.fromDate(date) + implicit def stringNetworkOps(str: String): StringNetworkOps = new StringNetworkOps(str) @@ -65,7 +68,7 @@ object CommonExpressionUtils { new String(byteArray) implicit object dateSplicer extends Splicer[JDate] { - def toString(date: Date) = + def toString(date: Date): String = if (date != null) date.format else null } diff --git a/scex-util/src/main/scala/com/avsystem/scex/util/CommonSymbolValidators.scala b/scex-util/src/main/scala/com/avsystem/scex/util/CommonSymbolValidators.scala index 61fea0f6..b262f125 100644 --- a/scex-util/src/main/scala/com/avsystem/scex/util/CommonSymbolValidators.scala +++ b/scex-util/src/main/scala/com/avsystem/scex/util/CommonSymbolValidators.scala @@ -134,6 +134,7 @@ object CommonSymbolValidators { on { so: StringNetworkOps => so.all.introduced.members } on { so: StringMiscOps => so.all.introduced.members } on { ed: EnrichedDate => ed.all.introduced.members } + on { ed: EnrichedZonedDate => ed.all.introduced.members } on { ea: EnrichedArray[_] => ea.all.introduced.members } on { su: StringUtil => su.all.members } diff --git a/scex-util/src/main/scala/com/avsystem/scex/util/EnrichedDate.scala b/scex-util/src/main/scala/com/avsystem/scex/util/EnrichedDate.scala index 92327380..8db51f58 100644 --- a/scex-util/src/main/scala/com/avsystem/scex/util/EnrichedDate.scala +++ b/scex-util/src/main/scala/com/avsystem/scex/util/EnrichedDate.scala @@ -8,19 +8,7 @@ import java.time.temporal.ChronoField import java.time.{Clock, ZoneId, ZonedDateTime} import java.util.{Calendar, Date} -final class EnrichedDate(wrapped: Date, zone: ZoneId = Clock.systemDefaultZone().getZone) { - private def zonedDateTime: ZonedDateTime = ZonedDateTime.ofInstant(wrapped.toInstant, zone) - - @Documentation("Formats the date using the default date format: yyyy.MM.dd HH:mm:ss.") - def format: String = - if (zone == Clock.systemDefaultZone().getZone) - CommonDateFormat.get.format(wrapped) - else - format(CommonDateFormat.get.toPattern) - - @Documentation("Formats the date according to provided date format. An example of correct date format is yyyy.MM.dd HH:mm:ss.") - def format(dateFormat: String): String = DateTimeFormatter.ofPattern(dateFormat).format(zonedDateTime) - +final class EnrichedDate(private val wrapped: Date) extends AnyVal { @Documentation("Adds the provided amount of milliseconds to the date.") def addMilliseconds(amount: Int): Date = DateUtils.addMilliseconds(wrapped, amount) @Documentation("Adds the provided amount of seconds to the date.") @@ -52,19 +40,34 @@ final class EnrichedDate(wrapped: Date, zone: ZoneId = Clock.systemDefaultZone() @Documentation("Returns the date as a number of milliseconds since 1970.01.01 00:00:00.") def millis: Long = wrapped.getTime +} + +final class EnrichedZonedDate(private val zonedDateTime: ZonedDateTime) extends AnyVal { + + @Documentation("Formats the date using the default date format: yyyy.MM.dd HH:mm:ss.") + def format: String = DateTimeFormatter.ofPattern(CommonDateFormat.get.toPattern).format(zonedDateTime) + + @Documentation("Formats the date according to provided date format. An example of correct date format is yyyy.MM.dd HH:mm:ss.") + def format(dateFormat: String): String = DateTimeFormatter.ofPattern(dateFormat).format(zonedDateTime) @Documentation("Returns the seconds field of the date expressed in milliseconds.") def millisOfSecond: Int = zonedDateTime.get(ChronoField.MILLI_OF_SECOND) + @Documentation("Returns the seconds field of the date.") def secondOfMinute: Int = zonedDateTime.get(ChronoField.SECOND_OF_MINUTE) + @Documentation("Returns the number of seconds which passed since 00:00 for the date.") def secondOfDay: Int = zonedDateTime.get(ChronoField.SECOND_OF_DAY) + @Documentation("Returns the minutes field of the date.") def minuteOfHour: Int = zonedDateTime.get(ChronoField.MINUTE_OF_HOUR) + @Documentation("Returns the number of minutes which passed since 00:00 for the date.") def minuteOfDay: Int = zonedDateTime.get(ChronoField.MINUTE_OF_DAY) + @Documentation("Returns the number of hours which passed since 00:00 for the date.") def hourOfDay: Int = zonedDateTime.get(ChronoField.HOUR_OF_DAY) + @Documentation("Returns the day field of the date.") def dayOfMonth: Int = zonedDateTime.get(ChronoField.DAY_OF_MONTH) @@ -78,3 +81,8 @@ final class EnrichedDate(wrapped: Date, zone: ZoneId = Clock.systemDefaultZone() def monthOfYear: Int = zonedDateTime.get(ChronoField.MONTH_OF_YEAR) } +object EnrichedZonedDate { + def fromDate(date: Date, zone: ZoneId = Clock.systemDefaultZone().getZone) = + new EnrichedZonedDate(ZonedDateTime.ofInstant(date.toInstant, zone)) +} + diff --git a/scex-util/src/test/scala/com/avsystem/scex/util/EnrichedDateTest.scala b/scex-util/src/test/scala/com/avsystem/scex/util/EnrichedDateTest.scala index ef2eeee3..04a05661 100644 --- a/scex-util/src/test/scala/com/avsystem/scex/util/EnrichedDateTest.scala +++ b/scex-util/src/test/scala/com/avsystem/scex/util/EnrichedDateTest.scala @@ -1,13 +1,17 @@ package com.avsystem.scex.util -import java.util.{Calendar, Date} import org.scalatest.funsuite.AnyFunSuite import java.time.ZoneId +import java.util.{Calendar, Date} class EnrichedDateTest extends AnyFunSuite { + + import CommonExpressionUtils.{enrichDate, enrichDateTime} + // 2019-04-17 12:55:14.456 UTC - val testDate: EnrichedDate = new EnrichedDate(new Date(1555505714456L), ZoneId.of("Europe/Warsaw")) + val testDate = new Date(1555505714456L) + val testZonedDate: EnrichedZonedDate = EnrichedZonedDate.fromDate(testDate, ZoneId.of("Europe/Warsaw")) def localMidnight: Calendar = { val time = Calendar.getInstance() From c9e4c8a1520a19d40b9632596e6030a3d542c4e9 Mon Sep 17 00:00:00 2001 From: Dawid Dworak Date: Fri, 30 Aug 2024 09:46:24 +0200 Subject: [PATCH 12/14] Default DateTimeFormatter in CommonDateFormat --- .../scala/com/avsystem/scex/util/CommonDateFormat.scala | 7 ++++++- .../main/scala/com/avsystem/scex/util/EnrichedDate.scala | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/scex-util/src/main/scala/com/avsystem/scex/util/CommonDateFormat.scala b/scex-util/src/main/scala/com/avsystem/scex/util/CommonDateFormat.scala index 0e56fed7..3e45c8a7 100644 --- a/scex-util/src/main/scala/com/avsystem/scex/util/CommonDateFormat.scala +++ b/scex-util/src/main/scala/com/avsystem/scex/util/CommonDateFormat.scala @@ -1,13 +1,18 @@ package com.avsystem.scex.util import java.text.SimpleDateFormat +import java.time.format.DateTimeFormatter // performance optimization, see https://github.com/AVSystem/scex/pull/38#discussion_r1724465654 object CommonDateFormat { + final val DefaultPattern = "yyyy.MM.dd HH:mm:ss" + + final val Formatter: DateTimeFormatter = DateTimeFormatter.ofPattern(DefaultPattern) + // thread-local instance of SimpleDateFormat because it's not thread-safe private val dateFormatTL = new ThreadLocal[SimpleDateFormat] { override def initialValue(): SimpleDateFormat = { - val df = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss") + val df = new SimpleDateFormat(DefaultPattern) df.setLenient(false) df } diff --git a/scex-util/src/main/scala/com/avsystem/scex/util/EnrichedDate.scala b/scex-util/src/main/scala/com/avsystem/scex/util/EnrichedDate.scala index 8db51f58..d035ff2e 100644 --- a/scex-util/src/main/scala/com/avsystem/scex/util/EnrichedDate.scala +++ b/scex-util/src/main/scala/com/avsystem/scex/util/EnrichedDate.scala @@ -45,7 +45,7 @@ final class EnrichedDate(private val wrapped: Date) extends AnyVal { final class EnrichedZonedDate(private val zonedDateTime: ZonedDateTime) extends AnyVal { @Documentation("Formats the date using the default date format: yyyy.MM.dd HH:mm:ss.") - def format: String = DateTimeFormatter.ofPattern(CommonDateFormat.get.toPattern).format(zonedDateTime) + def format: String = CommonDateFormat.Formatter.format(zonedDateTime) @Documentation("Formats the date according to provided date format. An example of correct date format is yyyy.MM.dd HH:mm:ss.") def format(dateFormat: String): String = DateTimeFormatter.ofPattern(dateFormat).format(zonedDateTime) From cb023fa7d213e90c9d05c20ae0da8923f4154cf7 Mon Sep 17 00:00:00 2001 From: Dawid Dworak Date: Fri, 30 Aug 2024 09:51:36 +0200 Subject: [PATCH 13/14] Update dependencies --- build.sbt | 6 +++--- project/build.properties | 2 +- project/plugins.sbt | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build.sbt b/build.sbt index e5009a52..f23472cc 100644 --- a/build.sbt +++ b/build.sbt @@ -24,10 +24,10 @@ val CompileAndTest = "compile->compile;test->test" val parserCombinatorsVersion = "2.4.0" val avsCommonsVersion = "2.18.0" val slf4jVersion = "2.0.16" -val logbackVersion = "1.5.6" // Tests only -val commonsLang3Version = "3.16.0" +val logbackVersion = "1.5.7" // Tests only +val commonsLang3Version = "3.17.0" val commonsCodecVersion = "1.17.1" -val guavaVersion = "33.2.1-jre" +val guavaVersion = "33.3.0-jre" val commonsNetVersion = "3.11.1" val scalatestVersion = "3.2.19" diff --git a/project/build.properties b/project/build.properties index e4337a74..ebb337b0 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1,2 +1,2 @@ # suppress inspection "UnusedProperty" -sbt.version=1.9.8 +sbt.version=1.10.1 diff --git a/project/plugins.sbt b/project/plugins.sbt index 55bbebc6..ce6c94fc 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,5 +1,5 @@ logLevel := Level.Warn addSbtPlugin("org.jetbrains.scala" % "sbt-ide-settings" % "1.1.2") -addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.5.12") +addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.6.1") addSbtPlugin("com.github.sbt" % "sbt-github-actions" % "0.24.0") \ No newline at end of file From 1a290aedacb1645d3ab0550f74f29ce8998558bd Mon Sep 17 00:00:00 2001 From: Dawid Dworak Date: Fri, 30 Aug 2024 09:58:19 +0200 Subject: [PATCH 14/14] Fix tests on CI --- .../avsystem/scex/util/EnrichedDateTest.scala | 40 ++++++++++--------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/scex-util/src/test/scala/com/avsystem/scex/util/EnrichedDateTest.scala b/scex-util/src/test/scala/com/avsystem/scex/util/EnrichedDateTest.scala index 04a05661..e3a5bdfc 100644 --- a/scex-util/src/test/scala/com/avsystem/scex/util/EnrichedDateTest.scala +++ b/scex-util/src/test/scala/com/avsystem/scex/util/EnrichedDateTest.scala @@ -7,10 +7,12 @@ import java.util.{Calendar, Date} class EnrichedDateTest extends AnyFunSuite { - import CommonExpressionUtils.{enrichDate, enrichDateTime} + import CommonExpressionUtils.enrichDate // 2019-04-17 12:55:14.456 UTC val testDate = new Date(1555505714456L) + + // enrichZonedDate tested explicitly to provide stable zone for tests val testZonedDate: EnrichedZonedDate = EnrichedZonedDate.fromDate(testDate, ZoneId.of("Europe/Warsaw")) def localMidnight: Calendar = { @@ -22,14 +24,6 @@ class EnrichedDateTest extends AnyFunSuite { time } - test("format with default format") { - assert(testDate.format == "2019.04.17 14:55:14") - } - - test("format with custom format") { - assert(testDate.format("yyyy-MM-dd") == "2019-04-17") - } - test("addMilliseconds") { assert(testDate.addMilliseconds(1000) == new Date(1555505715456L)) } @@ -100,44 +94,52 @@ class EnrichedDateTest extends AnyFunSuite { assert(testDate.millis == 1555505714456L) } + test("format with default format") { + assert(testZonedDate.format == "2019.04.17 14:55:14") + } + + test("format with custom format") { + assert(testZonedDate.format("yyyy-MM-dd") == "2019-04-17") + } + test("millisOfSecond") { - assert(testDate.millisOfSecond == 456) + assert(testZonedDate.millisOfSecond == 456) } test("secondOfMinute") { - assert(testDate.secondOfMinute == 14) + assert(testZonedDate.secondOfMinute == 14) } test("secondOfDay") { - assert(testDate.secondOfDay == 53714) + assert(testZonedDate.secondOfDay == 53714) } test("minuteOfHour") { - assert(testDate.minuteOfHour == 55) + assert(testZonedDate.minuteOfHour == 55) } test("minuteOfDay") { - assert(testDate.minuteOfDay == 895) + assert(testZonedDate.minuteOfDay == 895) } test("hourOfDay") { - assert(testDate.hourOfDay == 14) + assert(testZonedDate.hourOfDay == 14) } test("dayOfMonth") { - assert(testDate.dayOfMonth == 17) + assert(testZonedDate.dayOfMonth == 17) } test("dayOfWeek") { - assert(testDate.dayOfWeek == 3) + assert(testZonedDate.dayOfWeek == 3) } test("dayOfYear") { - assert(testDate.dayOfYear == 107) + assert(testZonedDate.dayOfYear == 107) } test("monthOfYear") { - assert(testDate.monthOfYear == 4) + assert(testZonedDate.monthOfYear == 4) } }