11package fr .insee .vtl .engine .expressions ;
22
3+ import fr .insee .vtl .engine .exceptions .CastException ;
34import fr .insee .vtl .engine .exceptions .InvalidArgumentException ;
5+ import fr .insee .vtl .engine .exceptions .VtlRuntimeException ;
46import fr .insee .vtl .model .Positioned ;
57import fr .insee .vtl .model .ResolvableExpression ;
68import fr .insee .vtl .model .exceptions .VtlScriptException ;
@@ -76,7 +78,8 @@ public ResolvableExpression castBoolean(ResolvableExpression expr) {
7678 return exprValue ? 1D : 0D ;
7779 });
7880 }
79- throw new ClassCastException ("Cast Boolean to " + outputClass + isNotSupported );
81+ throw new VtlRuntimeException (
82+ new CastException ("Cast Boolean to " + outputClass + isNotSupported , this ));
8083 }
8184
8285 private ResolvableExpression castDouble (ResolvableExpression expr ) {
@@ -98,8 +101,9 @@ private ResolvableExpression castDouble(ResolvableExpression expr) {
98101 Double exprValue = (Double ) expr .resolve (context );
99102 if (exprValue == null ) return null ;
100103 if (exprValue % 1 != 0 )
101- throw new UnsupportedOperationException (
102- exprValue + " can not be casted into integer" );
104+ throw new VtlRuntimeException (
105+ new CastException (
106+ exprValue + " can not be casted into integer" , CastExpression .this ));
103107 return exprValue .longValue ();
104108 });
105109 if (outputClass .equals (Double .class ))
@@ -115,7 +119,8 @@ private ResolvableExpression castDouble(ResolvableExpression expr) {
115119 if (exprValue == null ) return null ;
116120 return !exprValue .equals (0D );
117121 });
118- throw new ClassCastException ("Cast Double to " + outputClass + isNotSupported );
122+ throw new VtlRuntimeException (
123+ new CastException ("Cast Double to " + outputClass + isNotSupported , this ));
119124 }
120125
121126 private ResolvableExpression castInstant (ResolvableExpression expr , String mask ) {
@@ -125,18 +130,24 @@ private ResolvableExpression castInstant(ResolvableExpression expr, String mask)
125130 .withPosition (expr )
126131 .using (
127132 context -> {
128- var value = expr .resolve (context );
129- Instant exprValue ;
130- if (value instanceof LocalDate date ) {
131- exprValue = date .atStartOfDay ().toInstant (ZoneOffset .UTC );
132- } else {
133- exprValue = (Instant ) value ;
133+ try {
134+ var value = expr .resolve (context );
135+ Instant exprValue ;
136+ if (value instanceof LocalDate date ) {
137+ exprValue = date .atStartOfDay ().toInstant (ZoneOffset .UTC );
138+ } else {
139+ exprValue = (Instant ) value ;
140+ }
141+ if (exprValue == null ) return null ;
142+ DateTimeFormatter maskFormatter = DateTimeFormatter .ofPattern (mask );
143+ return maskFormatter .format (exprValue .atOffset (ZoneOffset .UTC ));
144+ } catch (Exception e ) {
145+ throw new VtlRuntimeException (
146+ new CastException ("Failed to cast date to string" , e , CastExpression .this ));
134147 }
135- if (exprValue == null ) return null ;
136- DateTimeFormatter maskFormatter = DateTimeFormatter .ofPattern (mask );
137- return maskFormatter .format (exprValue .atOffset (ZoneOffset .UTC ));
138148 });
139- throw new ClassCastException ("Cast Date to " + outputClass + isNotSupported );
149+ throw new VtlRuntimeException (
150+ new CastException ("Cast Date to " + outputClass + isNotSupported , this ));
140151 }
141152
142153 private ResolvableExpression castLong (ResolvableExpression expr ) {
@@ -168,7 +179,8 @@ private ResolvableExpression castLong(ResolvableExpression expr) {
168179 if (exprValue == null ) return null ;
169180 return !exprValue .equals (0L );
170181 });
171- throw new ClassCastException ("Cast Long to " + outputClass + isNotSupported );
182+ throw new VtlRuntimeException (
183+ new CastException ("Cast Long to " + outputClass + isNotSupported , this ));
172184 }
173185
174186 private ResolvableExpression castString (ResolvableExpression expr , String mask ) {
@@ -178,18 +190,54 @@ private ResolvableExpression castString(ResolvableExpression expr, String mask)
178190 .withPosition (expr )
179191 .using (
180192 context -> {
181- String exprValue = (String ) expr .resolve (context );
182- if (exprValue == null ) return null ;
183- return Long .valueOf (exprValue );
193+ String exprValue = null ;
194+ try {
195+ exprValue = (String ) expr .resolve (context );
196+ if (exprValue == null ) return null ;
197+ if (exprValue .isEmpty ()) {
198+ throw new VtlRuntimeException (
199+ new CastException (
200+ "Cannot cast empty string \" \" to integer" , CastExpression .this ));
201+ }
202+ return Long .valueOf (exprValue );
203+ } catch (VtlRuntimeException e ) {
204+ // Re-throw VtlRuntimeException as-is
205+ throw e ;
206+ } catch (NumberFormatException e ) {
207+ String value = exprValue != null ? exprValue : "null" ;
208+ throw new VtlRuntimeException (
209+ new CastException (
210+ "Failed to cast string \" " + value + "\" to integer" ,
211+ e ,
212+ CastExpression .this ));
213+ }
184214 });
185215 } else if (outputClass .equals (Double .class )) {
186216 return ResolvableExpression .withType (Double .class )
187217 .withPosition (expr )
188218 .using (
189219 context -> {
190- String exprValue = (String ) expr .resolve (context );
191- if (exprValue == null ) return null ;
192- return Double .valueOf (exprValue );
220+ String exprValue = null ;
221+ try {
222+ exprValue = (String ) expr .resolve (context );
223+ if (exprValue == null ) return null ;
224+ if (exprValue .isEmpty ()) {
225+ throw new VtlRuntimeException (
226+ new CastException (
227+ "Cannot cast empty string \" \" to number" , CastExpression .this ));
228+ }
229+ return Double .valueOf (exprValue );
230+ } catch (VtlRuntimeException e ) {
231+ // Re-throw VtlRuntimeException as-is
232+ throw e ;
233+ } catch (NumberFormatException e ) {
234+ String value = exprValue != null ? exprValue : "null" ;
235+ throw new VtlRuntimeException (
236+ new CastException (
237+ "Failed to cast string \" " + value + "\" to number" ,
238+ e ,
239+ CastExpression .this ));
240+ }
193241 });
194242 } else if (outputClass .equals (Boolean .class )) {
195243 return ResolvableExpression .withType (Boolean .class )
@@ -205,42 +253,76 @@ private ResolvableExpression castString(ResolvableExpression expr, String mask)
205253 .withPosition (expr )
206254 .using (
207255 context -> {
208- if (mask == null ) return null ;
209- String exprValue = (String ) expr .resolve (context );
210- if (exprValue == null ) return null ;
211- // The spec is pretty vague about date and time. Apparently, date is a point in time
212- // so a good java
213- // representation is Instant. But date can be created using only year/month and date
214- // mask, leaving
215- // any time information.
216- DateTimeFormatter maskFormatter =
217- DateTimeFormatter .ofPattern (mask ).withZone (ZoneOffset .UTC );
256+ String exprValue = null ;
218257 try {
219- return LocalDateTime .parse (exprValue , maskFormatter ).toInstant (ZoneOffset .UTC );
220- } catch (DateTimeParseException dtp ) {
221- return LocalDate .parse (exprValue , maskFormatter )
222- .atStartOfDay ()
223- .toInstant (ZoneOffset .UTC );
258+ if (mask == null ) return null ;
259+ exprValue = (String ) expr .resolve (context );
260+ if (exprValue == null ) return null ;
261+ if (exprValue .isEmpty ()) {
262+ throw new VtlRuntimeException (
263+ new CastException (
264+ "Cannot cast empty string \" \" to date (mask: \" " + mask + "\" )" ,
265+ CastExpression .this ));
266+ }
267+ // The spec is pretty vague about date and time. Apparently, date is a point
268+ // in time so a good Java representation is Instant. But date can be created
269+ // using only year/month and date mask, leaving any time information.
270+ DateTimeFormatter maskFormatter =
271+ DateTimeFormatter .ofPattern (mask ).withZone (ZoneOffset .UTC );
272+ try {
273+ return LocalDateTime .parse (exprValue , maskFormatter ).toInstant (ZoneOffset .UTC );
274+ } catch (DateTimeParseException dtp ) {
275+ return LocalDate .parse (exprValue , maskFormatter )
276+ .atStartOfDay ()
277+ .toInstant (ZoneOffset .UTC );
278+ }
279+ } catch (VtlRuntimeException e ) {
280+ // Re-throw VtlRuntimeException as-is
281+ throw e ;
282+ } catch (Exception e ) {
283+ String value = exprValue != null ? exprValue : "null" ;
284+ throw new VtlRuntimeException (
285+ new CastException (
286+ "Failed to cast string \" "
287+ + value
288+ + "\" to date with mask \" "
289+ + mask
290+ + "\" " ,
291+ e ,
292+ CastExpression .this ));
224293 }
225294 });
226295 } else if (outputClass .equals (PeriodDuration .class )) {
227296 return ResolvableExpression .withType (PeriodDuration .class )
228297 .withPosition (expr )
229298 .using (
230299 context -> {
231- String value = (String ) expr .tryCast (String .class ).resolve (context );
232- return PeriodDuration .parse (value ).normalizedYears ().normalizedStandardDays ();
300+ try {
301+ String value = (String ) expr .tryCast (String .class ).resolve (context );
302+ return PeriodDuration .parse (value ).normalizedYears ().normalizedStandardDays ();
303+ } catch (Exception e ) {
304+ throw new VtlRuntimeException (
305+ new CastException (
306+ "Failed to cast string to duration" , e , CastExpression .this ));
307+ }
233308 });
234309 } else if (outputClass .equals (Interval .class )) {
235310 return ResolvableExpression .withType (Interval .class )
236311 .withPosition (expr )
237312 .using (
238313 context -> {
239- String value = (String ) expr .tryCast (String .class ).resolve (context );
240- return Interval .parse (value );
314+ try {
315+ String value = (String ) expr .tryCast (String .class ).resolve (context );
316+ return Interval .parse (value );
317+ } catch (Exception e ) {
318+ throw new VtlRuntimeException (
319+ new CastException (
320+ "Failed to cast string to interval" , e , CastExpression .this ));
321+ }
241322 });
242323 } else {
243- throw new ClassCastException ("Cast String to " + outputClass + isNotSupported );
324+ throw new VtlRuntimeException (
325+ new CastException ("Cast String to " + outputClass + isNotSupported , this ));
244326 }
245327 }
246328
0 commit comments