Skip to content
This repository was archived by the owner on Jan 22, 2019. It is now read-only.

Commit ad36414

Browse files
committed
Fix #142
1 parent 43835ca commit ad36414

File tree

4 files changed

+156
-11
lines changed

4 files changed

+156
-11
lines changed

release-notes/CREDITS

+5
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,8 @@ George Fraser (georgewfraser@github)
9797
* Contributed #127: Add `CsvGenerator.Feature.ALWAYS_QUOTE_EMPTY_STRINGS` to allow forced
9898
quoting of empty Strings.
9999
(2.9.0)
100+
101+
Austin Sharp (sharpau@github)
102+
103+
* Suggested #142: Add methods for appending columns of a `CsvSchema` into another
104+
(2.9.0)

release-notes/VERSION

+3-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ Project: jackson-dataformat-csv
1212
#130: Add fluent addColumns operation to CsvSchema.Builder
1313
(contributed by Peter A)
1414
#139: Add `CsvParser.Feature.ALLOW_TRAILING_COMMA` to allow enforcing strict handling
15-
(contributed by Nick B)
15+
(contributed by Nick B)
16+
#142: Add methods for appending columns of a `CsvSchema` into another
17+
(suggested by Austin S)
1618

1719
2.8.6 (12-Jan-2017)
1820
2.8.5 (14-Nov-2016)

src/main/java/com/fasterxml/jackson/dataformat/csv/CsvSchema.java

+98-8
Original file line numberDiff line numberDiff line change
@@ -453,21 +453,37 @@ public Builder(CsvSchema src)
453453
_anyPropertyName = src._anyPropertyName;
454454
}
455455

456+
/**
457+
* NOTE: does NOT check for duplicate column names so it is possibly to
458+
* accidentally add duplicates.
459+
*/
456460
public Builder addColumn(String name) {
457461
int index = _columns.size();
458462
return addColumn(new Column(index, name));
459463
}
464+
465+
/**
466+
* NOTE: does NOT check for duplicate column names so it is possibly to
467+
* accidentally add duplicates.
468+
*/
460469
public Builder addColumn(String name, ColumnType type) {
461470
int index = _columns.size();
462471
return addColumn(new Column(index, name, type));
463472
}
464473

474+
/**
475+
* NOTE: does NOT check for duplicate column names so it is possibly to
476+
* accidentally add duplicates.
477+
*/
465478
public Builder addColumn(Column c) {
466479
_columns.add(c);
467480
return this;
468481
}
469482

470483
/**
484+
* NOTE: does NOT check for duplicate column names so it is possibly to
485+
* accidentally add duplicates.
486+
*
471487
* @since 2.9
472488
*/
473489
public Builder addColumns(Iterable<Column> cs) {
@@ -478,6 +494,9 @@ public Builder addColumns(Iterable<Column> cs) {
478494
}
479495

480496
/**
497+
* NOTE: does NOT check for duplicate column names so it is possibly to
498+
* accidentally add duplicates.
499+
*
481500
* @since 2.9
482501
*/
483502
public Builder addColumns(Iterable<String> names, ColumnType type) {
@@ -488,6 +507,24 @@ public Builder addColumns(Iterable<String> names, ColumnType type) {
488507
return result;
489508
}
490509

510+
/**
511+
* NOTE: unlike many other add methods, this method DOES check for, and
512+
* discard, possible duplicate columns: that is, if this builder already
513+
* has a column with same name as column to be added, existing column
514+
* is retained and new column ignored.
515+
*
516+
* @since 2.9
517+
*/
518+
public Builder addColumnsFrom(CsvSchema schema) {
519+
Builder result = this;
520+
for (Column col : schema) {
521+
if (!hasColumn(col.getName())) {
522+
result = result.addColumn(col);
523+
}
524+
}
525+
return result;
526+
}
527+
491528
public Builder addArrayColumn(String name) {
492529
int index = _columns.size();
493530
return addColumn(new Column(index, name, ColumnType.ARRAY, ""));
@@ -577,7 +614,25 @@ public int size() {
577614
public Iterator<Column> getColumns() {
578615
return _columns.iterator();
579616
}
580-
617+
618+
/**
619+
*<p>
620+
* NOTE: this method requires linear scan over existing columns
621+
* so it may be more efficient to use other types of lookups if
622+
* available (for example, {@link CsvSchema#column(String)} has a
623+
* hash lookup to use).
624+
*
625+
* @since 2.9
626+
*/
627+
public boolean hasColumn(String name) {
628+
for (int i = 0, end = _columns.size(); i < end; ++i) {
629+
if (_columns.get(i).getName().equals(name)) {
630+
return true;
631+
}
632+
}
633+
return false;
634+
}
635+
581636
/**
582637
* Method for specifying whether Schema should indicate that
583638
* a header line (first row that contains column names) is to be
@@ -1114,10 +1169,7 @@ public CsvSchema withoutEscapeChar() {
11141169
*/
11151170
@Deprecated // in 2.7; remove in 2.8
11161171
public CsvSchema withArrayElementSeparator(char c) {
1117-
return (Character.toString(c).equals(_arrayElementSeparator)) ? this
1118-
: new CsvSchema(_columns, _features,
1119-
_columnSeparator, _quoteChar, _escapeChar, _lineSeparator, Character.toString(c),
1120-
_nullValue, _columnsByName, _anyPropertyName);
1172+
return withArrayElementSeparator( Character.toString(c));
11211173
}
11221174

11231175
/**
@@ -1157,13 +1209,40 @@ public CsvSchema withNullValue(String nvl) {
11571209
(nvl == null) ? null : nvl.toCharArray(),
11581210
_columnsByName, _anyPropertyName);
11591211
}
1160-
1212+
11611213
public CsvSchema withoutColumns() {
11621214
return new CsvSchema(NO_COLUMNS, _features,
11631215
_columnSeparator, _quoteChar, _escapeChar, _lineSeparator, _arrayElementSeparator,
11641216
_nullValue, _columnsByName, _anyPropertyName);
11651217
}
11661218

1219+
/**
1220+
* Mutant factory method that will try to combine columns of this schema with those
1221+
* from `toAppend`, starting with columns of this instance, and ignoring
1222+
* duplicates (if any) from argument `toAppend`.
1223+
* All settings aside from column sets are copied from `this` instance.
1224+
*<p>
1225+
* As with all `withXxx()` methods this method never modifies `this` but either
1226+
* returns it unmodified (if no new columns found from `toAppend`), or constructs
1227+
* a new instance and returns that.
1228+
*
1229+
* @since 2.9
1230+
*/
1231+
public CsvSchema withColumnsFrom(CsvSchema toAppend) {
1232+
int addCount = toAppend.size();
1233+
if (addCount == 0) {
1234+
return this;
1235+
}
1236+
Builder b = rebuild();
1237+
for (int i = 0; i < addCount; ++i) {
1238+
Column col = toAppend.column(i);
1239+
if (column(col.getName()) == null) {
1240+
b.addColumn(col);
1241+
}
1242+
}
1243+
return b.build();
1244+
}
1245+
11671246
/**
11681247
* @since 2.7
11691248
*/
@@ -1318,9 +1397,20 @@ public String getNullValueString() {
13181397
public Iterator<Column> iterator() {
13191398
return Arrays.asList(_columns).iterator();
13201399
}
1321-
1400+
1401+
/**
1402+
* Accessor for finding out how many columns this schema defines.
1403+
*
1404+
* @return Number of columns this schema defines
1405+
*/
13221406
public int size() { return _columns.length; }
1323-
1407+
1408+
/**
1409+
* Accessor for column at specified index (0-based); index having to be within
1410+
*<pre>
1411+
* 0 &lt;= index &lt; size()
1412+
*</pre>
1413+
*/
13241414
public Column column(int index) {
13251415
return _columns[index];
13261416
}

src/test/java/com/fasterxml/jackson/dataformat/csv/schema/SchemaTest.java

+50-2
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,25 @@ static class ArrayWrapper {
2222
public List<String> c;
2323
}
2424

25-
// for [databind#74]
25+
// for [dataformat-csv#74]
2626
static class Point {
2727
public int y;
2828
public int x;
2929
}
3030

3131
@JsonPropertyOrder()
3232
public static class PointWithAnnotation extends Point {}
33+
34+
// for [dataformat-csv#142]
35+
interface Named {
36+
public String getFirstName();
37+
public String getLastName();
38+
}
39+
40+
static abstract class YZ {
41+
public abstract int getY();
42+
public abstract int getZ();
43+
}
3344

3445
/*
3546
/**********************************************************************
@@ -133,7 +144,7 @@ private void _verifyLinks(CsvSchema schema)
133144
prev = curr;
134145
}
135146
}
136-
147+
137148
// For [dataformat-csv#74]: problems applying default do-sort handling
138149
public void testSchemaWithOrdering() throws Exception
139150
{
@@ -158,4 +169,41 @@ public void testSchemaWithReordering()
158169
CsvSchema schemaWithoutReordering = schemaWithReordering.withColumnReordering(false);
159170
assertFalse(schemaWithoutReordering.reordersColumns());
160171
}
172+
173+
// For [dataformat-csv#142]: append columns from POJOs
174+
public void testSchemaComposition() throws Exception
175+
{
176+
CsvSchema pointSchema = MAPPER.typedSchemaFor(Point.class);
177+
CsvSchema yzSchema = MAPPER.typedSchemaFor(YZ.class);
178+
CsvSchema namedSchema = MAPPER.typedSchemaFor(Named.class);
179+
180+
// should only add `z` since there's already `y`
181+
CsvSchema schema = pointSchema;
182+
schema = schema.withColumnsFrom(yzSchema);
183+
// but then two name properties
184+
schema = schema.withColumnsFrom(namedSchema);
185+
186+
assertEquals(5, schema.size());
187+
Iterator<CsvSchema.Column> it = schema.iterator();
188+
assertEquals("x", it.next().getName());
189+
assertEquals("y", it.next().getName());
190+
assertEquals("z", it.next().getName());
191+
assertEquals("firstName", it.next().getName());
192+
assertEquals("lastName", it.next().getName());
193+
194+
// and try alternate way as well.
195+
CsvSchema.Builder builder = CsvSchema.builder();
196+
builder.addColumnsFrom(yzSchema)
197+
.addColumnsFrom(namedSchema)
198+
.addColumnsFrom(pointSchema);
199+
schema = builder.build();
200+
201+
assertEquals(5, schema.size());
202+
it = schema.iterator();
203+
assertEquals("y", it.next().getName());
204+
assertEquals("z", it.next().getName());
205+
assertEquals("firstName", it.next().getName());
206+
assertEquals("lastName", it.next().getName());
207+
assertEquals("x", it.next().getName());
208+
}
161209
}

0 commit comments

Comments
 (0)