Skip to content

Commit c603758

Browse files
Kevin SeimKevin Seim
authored andcommitted
Added support for validating marshalled fields (gc0096)
1 parent 44fde91 commit c603758

File tree

9 files changed

+181
-6
lines changed

9 files changed

+181
-6
lines changed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ configurations {
4242

4343
dependencies {
4444
testCompile group: 'junit', name: 'junit', version: '4.11'
45-
testCompile group: 'org.codehaus.groovy', name: 'groovy-all', version: '1.8.9'
45+
testCompile group: 'org.codehaus.groovy', name: 'groovy-all', version: '2.3.6'
4646
testRuntime group: 'commons-logging', name: 'commons-logging', version: '1.1.2'
4747
//emma group: 'emma', name: 'emma', version: '2.0.5312'
4848
//emma group: 'emma', name: 'emma', version: '2.0.5312'

changelog.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ Changes in version 3.0.0 (TBD)
66
* Requires JDK 1.7 or higher
77
* Removed support for ASM class rewriting
88
* Removed Spring framework support
9+
* Fixed IOUtil.getResource() to use ClassLoader argument (gc0109)
10+
* Added support for validating marshalled fields (gc0096)
911

1012
Changes in version 2.1.0 (2014-09-06)
1113
-------------------------------------

src/org/beanio/BeanWriter.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2010-2013 Kevin Seim
2+
* Copyright 2010-2014 Kevin Seim
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -38,6 +38,8 @@ public interface BeanWriter extends Debuggable {
3838
* or in a few other rare (but fatal) cases
3939
* @throws BeanWriterIOException if the underlying output stream throws an {@link IOException},
4040
* or if this writer is closed
41+
* @throws InvalidBeanException if BeanIO is configured to validate fields during marshalling,
42+
* and a field does not meet the configured validation rules
4143
*/
4244
public void write(Object bean) throws BeanWriterException, BeanWriterIOException;
4345

@@ -49,6 +51,8 @@ public interface BeanWriter extends Debuggable {
4951
* or in a few other rare (but fatal) cases
5052
* @throws BeanWriterIOException if the underlying output stream throws an {@link IOException},
5153
* or if this writer is closed
54+
* @throws InvalidBeanException if BeanIO is configured to validate fields during marshalling,
55+
* and a field does not meet the configured validation rules
5256
*/
5357
public void write(String recordName, Object bean) throws BeanWriterException;
5458

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright 2014 Kevin Seim
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.beanio;
17+
18+
/**
19+
* Exception thrown by a {@link BeanWriter} or {@link Marshaller}, when a bean
20+
* cannot be marshalled to meet the configured field validation rules.
21+
* @author Kevin Seim
22+
*/
23+
public class InvalidBeanException extends BeanWriterException {
24+
25+
private static final long serialVersionUID = 1L;
26+
27+
/**
28+
* Constructs a new InvalidBeanException.
29+
* @param message the error message
30+
*/
31+
public InvalidBeanException(String message) {
32+
super(message);
33+
}
34+
35+
/**
36+
* Constructs a new InvalidBeanException.
37+
* @param message the error message
38+
* @param cause the root cause
39+
*/
40+
public InvalidBeanException(String message, Throwable cause) {
41+
super(message, cause);
42+
}
43+
44+
/**
45+
* Constructs a new InvalidBeanException.
46+
* @param cause the root cause
47+
*/
48+
public InvalidBeanException(Throwable cause) {
49+
super(cause);
50+
}
51+
}

src/org/beanio/Marshaller.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2013 Kevin Seim
2+
* Copyright 2012-2014 Kevin Seim
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -52,6 +52,8 @@ public interface Marshaller extends Debuggable {
5252
* @return this <tt>Marshaller</tt>
5353
* @throws BeanWriterException if a record is not matched for the given bean object,
5454
* or in some other rare (but fatal) conditions
55+
* @throws InvalidBeanException if BeanIO is configured to validate fields during marshalling,
56+
* and a field does not meet the configured validation rules
5557
*/
5658
public Marshaller marshal(Object bean) throws BeanWriterException;
5759

@@ -62,6 +64,8 @@ public interface Marshaller extends Debuggable {
6264
* @return this <tt>Marshaller</tt>
6365
* @throws BeanWriterException if a record is not matched for the given record name
6466
* and bean object, or in some other rare (but fatal) conditions
67+
* @throws InvalidBeanException if BeanIO is configured to validate fields during marshalling,
68+
* and a field does not meet the configured validation rules
6569
*/
6670
public Marshaller marshal(String recordName, Object bean) throws BeanWriterException;
6771

src/org/beanio/internal/config/beanio.properties

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
## Copyright 2010-2013 Kevin Seim
1+
## Copyright 2010-2014 Kevin Seim
22
##
33
## Licensed under the Apache License, Version 2.0 (the "License");
44
## you may not use this file except in compliance with the License.
@@ -91,5 +91,8 @@ org.beanio.useDefaultIfMissing=true
9191
## whether non-public fields and methods can be made accessible
9292
org.beanio.allowProtectedAccess=true
9393

94+
## whether to validate marshalled fields
95+
org.beanio.validateOnMarshal=false
96+
9497
org.beanio.typeHandler.string-xml = org.beanio.types.StringTypeHandler
9598
org.beanio.typeHandler.int = org.beanio.types.IntegerTypeHandler

src/org/beanio/internal/parser/Field.java

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2011-2013 Kevin Seim
2+
* Copyright 2011-2014 Kevin Seim
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -38,6 +38,9 @@ public class Field extends ParserComponent implements Property {
3838

3939
private static final boolean USE_DEFAULT_IF_MISSING =
4040
Settings.getInstance().getBoolean(Settings.USE_DEFAULT_IF_MISSING);
41+
42+
private static final boolean VALIDATE_ON_MARSHAL =
43+
Settings.getInstance().getBoolean(Settings.VALIDATE_ON_MARSHAL);
4144

4245
private static final boolean marshalDefault =
4346
Settings.getInstance().getBoolean(Settings.DEFAULT_MARSHALLING_ENABLED);
@@ -194,6 +197,38 @@ public boolean marshal(MarshallingContext context) {
194197
text = formatValue(value);
195198
}
196199

200+
if (VALIDATE_ON_MARSHAL) {
201+
if (text == Value.NIL) {
202+
if (!format.isNillable()) {
203+
throw new InvalidBeanException("Invalid field '" + getName() + "', the value is not nillable");
204+
} else if (required) {
205+
throw new InvalidBeanException("Invalid field '" + getName() + "', a value is required");
206+
}
207+
}
208+
else if (text == null) {
209+
if (required) {
210+
throw new InvalidBeanException("Invalid field '" + getName() + "', a value is required");
211+
}
212+
}
213+
else {
214+
// validate minimum length
215+
if (text.length() < minLength) {
216+
throw new InvalidBeanException("Invalid field '" + getName() + "', '" +
217+
text + "' does not meet minimum length of " + minLength);
218+
}
219+
// validate maximum length
220+
if (text.length() > maxLength) {
221+
throw new InvalidBeanException("Invalid field '" + getName() + "', '" +
222+
text + "' exceeds maximum length of " + maxLength);
223+
}
224+
// validate the regular expression
225+
if (regex != null && !regex.matcher(text).matches()) {
226+
throw new InvalidBeanException("Invalid field '" + getName() + "', '" +
227+
text + "' does not match pattern '" + regex.pattern() + "'");
228+
}
229+
}
230+
}
231+
197232
format.insertField(context, text);
198233
return true;
199234
}

src/org/beanio/internal/util/Settings.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2010-2013 Kevin Seim
2+
* Copyright 2010-2014 Kevin Seim
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -125,6 +125,11 @@ public class Settings {
125125
* Whether non-public fields and methods may be made accessible.
126126
*/
127127
public static final String ALLOW_PROTECTED_PROPERTY_ACCESS = "org.beanio.allowProtectedAccess";
128+
/**
129+
* Whether to validate marshalled fields.
130+
* @since 3.0.0
131+
*/
132+
public static final String VALIDATE_ON_MARSHAL = "org.beanio.validateOnMarshal";
128133

129134
private static final String DEFAULT_CONFIGURATION_PATH = "org/beanio/internal/config/beanio.properties";
130135
private static final String DEFAULT_CONFIGURATION_FILENAME = "beanio.properties";
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package org.beanio.parser.validation
2+
3+
import org.beanio.*
4+
import org.beanio.parser.AbstractParserTest
5+
import org.junit.Ignore;
6+
import org.junit.Test
7+
8+
/**
9+
* Unit tests for marshalled field validation (which is disabled by default,
10+
* hence the Ignore unless configured).
11+
* @author Kevin Seim
12+
*/
13+
@Ignore
14+
class MarshalledFieldValidationTest extends AbstractParserTest {
15+
16+
@Test(expected=InvalidBeanException.class)
17+
void testRequired() {
18+
StreamFactory factory = createFactory("""
19+
<stream name="s" format="csv" strict="true">
20+
<record name="record" class="map">
21+
<field name="field" type="String" required="true" />
22+
</record>
23+
</stream>""");
24+
25+
Marshaller m = factory.createMarshaller("s")
26+
Map bean = ["field":null]
27+
m.marshal(bean)
28+
}
29+
30+
@Test(expected=InvalidBeanException.class)
31+
void testMinLength() {
32+
StreamFactory factory = createFactory("""
33+
<stream name="s" format="csv" strict="true">
34+
<record name="record" class="map">
35+
<field name="field" minLength="3" />
36+
</record>
37+
</stream>""");
38+
39+
Marshaller m = factory.createMarshaller("s")
40+
Map bean = ["field":"ab"]
41+
m.marshal(bean)
42+
}
43+
44+
@Test(expected=InvalidBeanException.class)
45+
void testMaxLength() {
46+
StreamFactory factory = createFactory("""
47+
<stream name="s" format="csv" strict="true">
48+
<record name="record" class="map">
49+
<field name="field" maxLength="3" />
50+
</record>
51+
</stream>""");
52+
53+
Marshaller m = factory.createMarshaller("s")
54+
Map bean = ["field":"abcd"]
55+
m.marshal(bean)
56+
}
57+
58+
@Test(expected=InvalidBeanException.class)
59+
void testRegex() {
60+
StreamFactory factory = createFactory("""
61+
<stream name="s" format="csv" strict="true">
62+
<record name="record" class="map">
63+
<field name="field" regex="\\d+" />
64+
</record>
65+
</stream>""");
66+
67+
Marshaller m = factory.createMarshaller("s")
68+
Map bean = ["field":"abc"]
69+
m.marshal(bean)
70+
}
71+
}

0 commit comments

Comments
 (0)