eldest)
22 | {
23 | return size() > _maxEntries;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/com/fasterxml/jackson/dataformat/csv/impl/NumberInput.java:
--------------------------------------------------------------------------------
1 | package com.fasterxml.jackson.dataformat.csv.impl;
2 |
3 | /* NOTE: copied from Jackson core, to reduce coupling
4 | */
5 | public final class NumberInput
6 | {
7 | /**
8 | * Textual representation of a double constant that can cause nasty problems
9 | * with JDK (see http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308).
10 | */
11 | public final static String NASTY_SMALL_DOUBLE = "2.2250738585072012e-308";
12 |
13 | /**
14 | * Constants needed for parsing longs from basic int parsing methods
15 | */
16 | final static long L_BILLION = 1000000000;
17 |
18 | final static String MIN_LONG_STR_NO_SIGN = String.valueOf(Long.MIN_VALUE).substring(1);
19 | final static String MAX_LONG_STR = String.valueOf(Long.MAX_VALUE);
20 |
21 | /**
22 | * Fast method for parsing integers that are known to fit into
23 | * regular 32-bit signed int type. This means that length is
24 | * between 1 and 9 digits (inclusive)
25 | *
26 | * Note: public to let unit tests call it
27 | */
28 | public final static int parseInt(char[] digitChars, int offset, int len)
29 | {
30 | int num = digitChars[offset] - '0';
31 | len += offset;
32 | // This looks ugly, but appears the fastest way (as per measurements)
33 | if (++offset < len) {
34 | num = (num * 10) + (digitChars[offset] - '0');
35 | if (++offset < len) {
36 | num = (num * 10) + (digitChars[offset] - '0');
37 | if (++offset < len) {
38 | num = (num * 10) + (digitChars[offset] - '0');
39 | if (++offset < len) {
40 | num = (num * 10) + (digitChars[offset] - '0');
41 | if (++offset < len) {
42 | num = (num * 10) + (digitChars[offset] - '0');
43 | if (++offset < len) {
44 | num = (num * 10) + (digitChars[offset] - '0');
45 | if (++offset < len) {
46 | num = (num * 10) + (digitChars[offset] - '0');
47 | if (++offset < len) {
48 | num = (num * 10) + (digitChars[offset] - '0');
49 | }
50 | }
51 | }
52 | }
53 | }
54 | }
55 | }
56 | }
57 | return num;
58 | }
59 |
60 | public final static long parseLong(char[] digitChars, int offset, int len)
61 | {
62 | // Note: caller must ensure length is [10, 18]
63 | int len1 = len-9;
64 | long val = parseInt(digitChars, offset, len1) * L_BILLION;
65 | return val + parseInt(digitChars, offset+len1, 9);
66 | }
67 |
68 | /**
69 | * Helper method for determining if given String representation of
70 | * an integral number would fit in 64-bit Java long or not.
71 | * Note that input String must NOT contain leading minus sign (even
72 | * if 'negative' is set to true).
73 | *
74 | * @param negative Whether original number had a minus sign (which is
75 | * NOT passed to this method) or not
76 | */
77 | public final static boolean inLongRange(char[] digitChars, int offset, int len,
78 | boolean negative)
79 | {
80 | String cmpStr = negative ? MIN_LONG_STR_NO_SIGN : MAX_LONG_STR;
81 | int cmpLen = cmpStr.length();
82 | if (len < cmpLen) return true;
83 | if (len > cmpLen) return false;
84 |
85 | for (int i = 0; i < cmpLen; ++i) {
86 | int diff = digitChars[offset+i] - cmpStr.charAt(i);
87 | if (diff != 0) {
88 | return (diff < 0);
89 | }
90 | }
91 | return true;
92 | }
93 |
94 | public final static double parseDouble(String numStr) throws NumberFormatException
95 | {
96 | // [JACKSON-486]: avoid some nasty float representations... but should it be MIN_NORMAL or MIN_VALUE?
97 | if (NASTY_SMALL_DOUBLE.equals(numStr)) {
98 | return Double.MIN_VALUE;
99 | }
100 | return Double.parseDouble(numStr);
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/src/main/java/com/fasterxml/jackson/dataformat/csv/impl/NumberOutput.java:
--------------------------------------------------------------------------------
1 | package com.fasterxml.jackson.dataformat.csv.impl;
2 |
3 | /**
4 | * Helper class for efficient writing of numeric values
5 | *
6 | * NOTE: modified from Jackson Core's similar functionality
7 | */
8 | public final class NumberOutput
9 | {
10 | private final static char NULL_CHAR = (char) 0;
11 |
12 | private static int MILLION = 1000000;
13 | private static int BILLION = 1000000000;
14 | private static long TEN_BILLION_L = 10000000000L;
15 | private static long THOUSAND_L = 1000L;
16 |
17 | private static long MIN_INT_AS_LONG = Integer.MIN_VALUE;
18 | private static long MAX_INT_AS_LONG = Integer.MAX_VALUE;
19 |
20 | private final static String SMALLEST_LONG = String.valueOf(Long.MIN_VALUE);
21 |
22 | private final static char[] LEADING_TRIPLETS = new char[4000];
23 | private final static char[] FULL_TRIPLETS = new char[4000];
24 | static {
25 | /* Let's fill it with NULLs for ignorable leading digits,
26 | * and digit chars for others
27 | */
28 | int ix = 0;
29 | for (int i1 = 0; i1 < 10; ++i1) {
30 | char f1 = (char) ('0' + i1);
31 | char l1 = (i1 == 0) ? NULL_CHAR : f1;
32 | for (int i2 = 0; i2 < 10; ++i2) {
33 | char f2 = (char) ('0' + i2);
34 | char l2 = (i1 == 0 && i2 == 0) ? NULL_CHAR : f2;
35 | for (int i3 = 0; i3 < 10; ++i3) {
36 | // Last is never to be empty
37 | char f3 = (char) ('0' + i3);
38 | LEADING_TRIPLETS[ix] = l1;
39 | LEADING_TRIPLETS[ix+1] = l2;
40 | LEADING_TRIPLETS[ix+2] = f3;
41 | FULL_TRIPLETS[ix] = f1;
42 | FULL_TRIPLETS[ix+1] = f2;
43 | FULL_TRIPLETS[ix+2] = f3;
44 | ix += 4;
45 | }
46 | }
47 | }
48 | }
49 |
50 | private final static byte[] FULL_TRIPLETS_B = new byte[4000];
51 | static {
52 | for (int i = 0; i < 4000; ++i) {
53 | FULL_TRIPLETS_B[i] = (byte) FULL_TRIPLETS[i];
54 | }
55 | }
56 |
57 | /*
58 | /**********************************************************
59 | /* Efficient serialization methods using raw buffers
60 | /**********************************************************
61 | */
62 |
63 | /**
64 | * @return Offset within buffer after outputting int
65 | */
66 | public static int outputInt(int value, char[] buffer, int offset)
67 | {
68 | if (value < 0) {
69 | if (value == Integer.MIN_VALUE) {
70 | /* Special case: no matching positive value within range;
71 | * let's then "upgrade" to long and output as such.
72 | */
73 | return outputLong(value, buffer, offset);
74 | }
75 | buffer[offset++] = '-';
76 | value = -value;
77 | }
78 |
79 | if (value < MILLION) { // at most 2 triplets...
80 | if (value < 1000) {
81 | if (value < 10) {
82 | buffer[offset++] = (char) ('0' + value);
83 | } else {
84 | offset = outputLeadingTriplet(value, buffer, offset);
85 | }
86 | } else {
87 | int thousands = value / 1000;
88 | value -= (thousands * 1000); // == value % 1000
89 | offset = outputLeadingTriplet(thousands, buffer, offset);
90 | offset = outputFullTriplet(value, buffer, offset);
91 | }
92 | return offset;
93 | }
94 |
95 | // ok, all 3 triplets included
96 | /* Let's first hand possible billions separately before
97 | * handling 3 triplets. This is possible since we know we
98 | * can have at most '2' as billion count.
99 | */
100 | boolean hasBillions = (value >= BILLION);
101 | if (hasBillions) {
102 | value -= BILLION;
103 | if (value >= BILLION) {
104 | value -= BILLION;
105 | buffer[offset++] = '2';
106 | } else {
107 | buffer[offset++] = '1';
108 | }
109 | }
110 | int newValue = value / 1000;
111 | int ones = (value - (newValue * 1000)); // == value % 1000
112 | value = newValue;
113 | newValue /= 1000;
114 | int thousands = (value - (newValue * 1000));
115 |
116 | // value now has millions, which have 1, 2 or 3 digits
117 | if (hasBillions) {
118 | offset = outputFullTriplet(newValue, buffer, offset);
119 | } else {
120 | offset = outputLeadingTriplet(newValue, buffer, offset);
121 | }
122 | offset = outputFullTriplet(thousands, buffer, offset);
123 | offset = outputFullTriplet(ones, buffer, offset);
124 | return offset;
125 | }
126 |
127 | /**
128 | * @return Offset within buffer after outputting int
129 | */
130 | public static int outputLong(long value, char[] buffer, int offset)
131 | {
132 | // First: does it actually fit in an int?
133 | if (value < 0L) {
134 | /* MIN_INT is actually printed as long, just because its
135 | * negation is not an int but long
136 | */
137 | if (value > MIN_INT_AS_LONG) {
138 | return outputInt((int) value, buffer, offset);
139 | }
140 | if (value == Long.MIN_VALUE) {
141 | // Special case: no matching positive value within range
142 | int len = SMALLEST_LONG.length();
143 | SMALLEST_LONG.getChars(0, len, buffer, offset);
144 | return (offset + len);
145 | }
146 | buffer[offset++] = '-';
147 | value = -value;
148 | } else {
149 | if (value <= MAX_INT_AS_LONG) {
150 | return outputInt((int) value, buffer, offset);
151 | }
152 | }
153 |
154 | /* Ok: real long print. Need to first figure out length
155 | * in characters, and then print in from end to beginning
156 | */
157 | int origOffset = offset;
158 | offset += calcLongStrLength(value);
159 | int ptr = offset;
160 |
161 | // First, with long arithmetics:
162 | while (value > MAX_INT_AS_LONG) { // full triplet
163 | ptr -= 3;
164 | long newValue = value / THOUSAND_L;
165 | int triplet = (int) (value - newValue * THOUSAND_L);
166 | outputFullTriplet(triplet, buffer, ptr);
167 | value = newValue;
168 | }
169 | // Then with int arithmetics:
170 | int ivalue = (int) value;
171 | while (ivalue >= 1000) { // still full triplet
172 | ptr -= 3;
173 | int newValue = ivalue / 1000;
174 | int triplet = ivalue - (newValue * 1000);
175 | outputFullTriplet(triplet, buffer, ptr);
176 | ivalue = newValue;
177 | }
178 | // And finally, if anything remains, partial triplet
179 | outputLeadingTriplet(ivalue, buffer, origOffset);
180 |
181 | return offset;
182 | }
183 |
184 | /*
185 | /**********************************************************
186 | /* Secondary convenience serialization methods
187 | /**********************************************************
188 | */
189 | /*
190 | final static String[] sSmallIntStrs = new String[] {
191 | "0","1","2","3","4","5","6","7","8","9","10"
192 | };
193 | final static String[] sSmallIntStrs2 = new String[] {
194 | "-1","-2","-3","-4","-5","-6","-7","-8","-9","-10"
195 | };
196 |
197 |
198 | public static String toString(int value)
199 | {
200 | // Lookup table for small values
201 | if (value < sSmallIntStrs.length) {
202 | if (value >= 0) {
203 | return sSmallIntStrs[value];
204 | }
205 | int v2 = -value - 1;
206 | if (v2 < sSmallIntStrs2.length) {
207 | return sSmallIntStrs2[v2];
208 | }
209 | }
210 | return Integer.toString(value);
211 | }
212 |
213 | public static String toString(long value)
214 | {
215 | if (value <= Integer.MAX_VALUE &&
216 | value >= Integer.MIN_VALUE) {
217 | return toString((int) value);
218 | }
219 | return Long.toString(value);
220 | }
221 | */
222 |
223 | public static String toString(double value)
224 | {
225 | return Double.toString(value);
226 | }
227 |
228 | public static String toString(float value)
229 | {
230 | return Float.toString(value);
231 | }
232 |
233 | /*
234 | /**********************************************************
235 | /* Internal methods
236 | /**********************************************************
237 | */
238 |
239 | private static int outputLeadingTriplet(int triplet, char[] buffer, int offset)
240 | {
241 | int digitOffset = (triplet << 2);
242 | char c = LEADING_TRIPLETS[digitOffset++];
243 | if (c != NULL_CHAR) {
244 | buffer[offset++] = c;
245 | }
246 | c = LEADING_TRIPLETS[digitOffset++];
247 | if (c != NULL_CHAR) {
248 | buffer[offset++] = c;
249 | }
250 | // Last is required to be non-empty
251 | buffer[offset++] = LEADING_TRIPLETS[digitOffset];
252 | return offset;
253 | }
254 |
255 | private static int outputFullTriplet(int triplet, char[] buffer, int offset)
256 | {
257 | int digitOffset = (triplet << 2);
258 | buffer[offset++] = FULL_TRIPLETS[digitOffset++];
259 | buffer[offset++] = FULL_TRIPLETS[digitOffset++];
260 | buffer[offset++] = FULL_TRIPLETS[digitOffset];
261 | return offset;
262 | }
263 |
264 | /**
265 | *
266 | * Pre-conditions: posValue is positive, and larger than
267 | * Integer.MAX_VALUE (about 2 billions).
268 | */
269 | private static int calcLongStrLength(long posValue)
270 | {
271 | int len = 10;
272 | long comp = TEN_BILLION_L;
273 |
274 | // 19 is longest, need to worry about overflow
275 | while (posValue >= comp) {
276 | if (len == 19) {
277 | break;
278 | }
279 | ++len;
280 | comp = (comp << 3) + (comp << 1); // 10x
281 | }
282 | return len;
283 | }
284 | }
285 |
--------------------------------------------------------------------------------
/src/main/resources/META-INF/LICENSE:
--------------------------------------------------------------------------------
1 | This copy of Jackson JSON processor databind module is licensed under the
2 | Apache (Software) License, version 2.0 ("the License").
3 | See the License for details about distribution rights, and the
4 | specific rights regarding derivate works.
5 |
6 | You may obtain a copy of the License at:
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
--------------------------------------------------------------------------------
/src/main/resources/META-INF/services/com.fasterxml.jackson.core.JsonFactory:
--------------------------------------------------------------------------------
1 | com.fasterxml.jackson.dataformat.csv.CsvFactory
2 |
--------------------------------------------------------------------------------
/src/test/java/com/fasterxml/jackson/dataformat/csv/FeaturesTest.java:
--------------------------------------------------------------------------------
1 | package com.fasterxml.jackson.dataformat.csv;
2 |
3 | import java.io.StringWriter;
4 |
5 | public class FeaturesTest extends ModuleTestBase
6 | {
7 | public void testFactoryFeatures() throws Exception
8 | {
9 | CsvFactory f = new CsvFactory();
10 | assertFalse(f.canHandleBinaryNatively());
11 | assertFalse(f.canUseCharArrays());
12 | assertTrue(f.canUseSchema(CsvSchema.emptySchema()));
13 |
14 | CsvParser p = f.createParser("");
15 | assertFalse(p.canReadObjectId());
16 | assertFalse(p.canReadTypeId());
17 | assertTrue(p.canUseSchema(CsvSchema.emptySchema()));
18 | p.close();
19 |
20 | CsvGenerator g = f.createGenerator(new StringWriter());
21 | assertFalse(g.canOmitFields());
22 | assertFalse(g.canWriteBinaryNatively());
23 | assertFalse(g.canWriteObjectId());
24 | assertFalse(g.canWriteTypeId());
25 | assertTrue(g.canWriteFormattedNumbers());
26 | assertTrue(g.canUseSchema(CsvSchema.emptySchema()));
27 | g.close();
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/test/java/com/fasterxml/jackson/dataformat/csv/ModuleTestBase.java:
--------------------------------------------------------------------------------
1 | package com.fasterxml.jackson.dataformat.csv;
2 |
3 | import java.io.IOException;
4 | import java.util.Arrays;
5 |
6 | import com.fasterxml.jackson.core.*;
7 | import com.fasterxml.jackson.annotation.JsonPropertyOrder;
8 |
9 | public abstract class ModuleTestBase extends junit.framework.TestCase
10 | {
11 | public enum Gender { MALE, FEMALE };
12 |
13 | /**
14 | * Slightly modified sample class from Jackson tutorial ("JacksonInFiveMinutes")
15 | */
16 | @JsonPropertyOrder({"firstName", "lastName", "gender" ,"verified", "userImage"})
17 | protected static class FiveMinuteUser {
18 |
19 | private Gender _gender;
20 |
21 | public String firstName, lastName;
22 |
23 | private boolean _isVerified;
24 | private byte[] _userImage;
25 |
26 | public FiveMinuteUser() { }
27 |
28 | public FiveMinuteUser(String first, String last, boolean verified, Gender g, byte[] data)
29 | {
30 | firstName = first;
31 | lastName = last;
32 | _isVerified = verified;
33 | _gender = g;
34 | _userImage = data;
35 | }
36 |
37 | public boolean isVerified() { return _isVerified; }
38 | public Gender getGender() { return _gender; }
39 | public byte[] getUserImage() { return _userImage; }
40 |
41 | public void setVerified(boolean b) { _isVerified = b; }
42 | public void setGender(Gender g) { _gender = g; }
43 | public void setUserImage(byte[] b) { _userImage = b; }
44 |
45 | @Override
46 | public boolean equals(Object o)
47 | {
48 | if (o == this) return true;
49 | if (o == null || o.getClass() != getClass()) return false;
50 | FiveMinuteUser other = (FiveMinuteUser) o;
51 | if (_isVerified != other._isVerified) return false;
52 | if (_gender != other._gender) return false;
53 | if (!firstName.equals(other.firstName)) return false;
54 | if (!lastName.equals(other.lastName)) return false;
55 | byte[] otherImage = other._userImage;
56 | if (otherImage.length != _userImage.length) return false;
57 | for (int i = 0, len = _userImage.length; i < len; ++i) {
58 | if (_userImage[i] != otherImage[i]) {
59 | return false;
60 | }
61 | }
62 | return true;
63 | }
64 |
65 | @Override
66 | public int hashCode() {
67 | // not really good but whatever:
68 | return firstName.hashCode();
69 | }
70 | }
71 |
72 | @JsonPropertyOrder({"id", "desc"})
73 | protected static class IdDesc {
74 | public String id, desc;
75 |
76 | protected IdDesc() { }
77 | public IdDesc(String id, String desc) {
78 | this.id = id;
79 | this.desc = desc;
80 | }
81 | }
82 |
83 | protected ModuleTestBase() { }
84 |
85 | /*
86 | /**********************************************************************
87 | /* Helper methods, setup
88 | /**********************************************************************
89 | */
90 |
91 | protected CsvMapper mapperForCsv()
92 | {
93 | return new CsvMapper();
94 | }
95 |
96 | /*
97 | /**********************************************************
98 | /* Helper methods; low-level
99 | /**********************************************************
100 | */
101 |
102 | public String quote(String str) {
103 | return '"'+str+'"';
104 | }
105 |
106 | protected String aposToQuotes(String json) {
107 | return json.replace("'", "\"");
108 | }
109 |
110 | protected void assertToken(JsonToken expToken, JsonToken actToken) {
111 | if (actToken != expToken) {
112 | fail("Expected token "+expToken+", current token "+actToken);
113 | }
114 | }
115 |
116 | protected void assertToken(JsonToken expToken, JsonParser jp) {
117 | assertToken(expToken, jp.getCurrentToken());
118 | }
119 |
120 | protected void assertType(Object ob, Class> expType)
121 | {
122 | if (ob == null) {
123 | fail("Expected an object of type "+expType.getName()+", got null");
124 | }
125 | Class> cls = ob.getClass();
126 | if (!expType.isAssignableFrom(cls)) {
127 | fail("Expected type "+expType.getName()+", got "+cls.getName());
128 | }
129 | }
130 |
131 | /**
132 | * Method that gets textual contents of the current token using
133 | * available methods, and ensures results are consistent, before
134 | * returning them
135 | */
136 | protected String getAndVerifyText(JsonParser jp)
137 | throws IOException, JsonParseException
138 | {
139 | // Ok, let's verify other accessors
140 | int actLen = jp.getTextLength();
141 | char[] ch = jp.getTextCharacters();
142 | String str2 = new String(ch, jp.getTextOffset(), actLen);
143 | String str = jp.getText();
144 |
145 | if (str.length() != actLen) {
146 | fail("Internal problem (jp.token == "+jp.getCurrentToken()+"): jp.getText().length() ['"+str+"'] == "+str.length()+"; jp.getTextLength() == "+actLen);
147 | }
148 | assertEquals("String access via getText(), getTextXxx() must be the same", str, str2);
149 |
150 | return str;
151 | }
152 |
153 | protected void verifyFieldName(JsonParser jp, String expName)
154 | throws IOException
155 | {
156 | assertEquals(expName, jp.getText());
157 | assertEquals(expName, jp.getCurrentName());
158 | }
159 |
160 | protected void verifyIntValue(JsonParser jp, long expValue)
161 | throws IOException
162 | {
163 | // First, via textual
164 | assertEquals(String.valueOf(expValue), jp.getText());
165 | }
166 |
167 | protected void verifyException(Throwable e, String... matches)
168 | {
169 | String msg = e.getMessage();
170 | String lmsg = (msg == null) ? "" : msg.toLowerCase();
171 | for (String match : matches) {
172 | String lmatch = match.toLowerCase();
173 | if (lmsg.indexOf(lmatch) >= 0) {
174 | return;
175 | }
176 | }
177 | fail("Expected an exception with one of substrings ("+Arrays.asList(matches)+"): got one with message \""+msg+"\"");
178 | }
179 | }
180 |
--------------------------------------------------------------------------------
/src/test/java/com/fasterxml/jackson/dataformat/csv/TestVersions.java:
--------------------------------------------------------------------------------
1 | package com.fasterxml.jackson.dataformat.csv;
2 |
3 | import java.io.*;
4 |
5 | import com.fasterxml.jackson.core.*;
6 | import com.fasterxml.jackson.databind.MapperFeature;
7 |
8 | public class TestVersions extends ModuleTestBase
9 | {
10 | public void testMapperVersions() throws IOException
11 | {
12 | CsvFactory f = new CsvFactory();
13 | assertVersion(f);
14 | CsvParser jp = (CsvParser) f.createParser("abc");
15 | assertVersion(jp);
16 | CsvGenerator jgen = f.createGenerator(new ByteArrayOutputStream());
17 | assertVersion(jgen);
18 | jp.close();
19 | jgen.close();
20 | }
21 |
22 | // Mostly to verify #11
23 | public void testMapperDefaults()
24 | {
25 | CsvMapper mapper = new CsvMapper();
26 | assertTrue(mapper.isEnabled(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY));
27 | }
28 |
29 | // Also, not strictly related to version but...
30 |
31 | public void testMapperCopy()
32 | {
33 | CsvMapper mapper = new CsvMapper();
34 | CsvMapper copy = mapper.copy();
35 | // for now, not throwing exception is a happy-enough case
36 | assertNotNull(copy);
37 | }
38 |
39 | /*
40 | /**********************************************************
41 | /* Helper methods
42 | /**********************************************************
43 | */
44 |
45 | private void assertVersion(Versioned vers)
46 | {
47 | assertEquals(PackageVersion.VERSION, vers.version());
48 | }
49 | }
50 |
51 |
--------------------------------------------------------------------------------
/src/test/java/com/fasterxml/jackson/dataformat/csv/deser/AnySetterTest.java:
--------------------------------------------------------------------------------
1 | package com.fasterxml.jackson.dataformat.csv.deser;
2 |
3 | import java.util.*;
4 |
5 | import com.fasterxml.jackson.annotation.JsonAnySetter;
6 | import com.fasterxml.jackson.dataformat.csv.*;
7 |
8 | public class AnySetterTest extends ModuleTestBase
9 | {
10 | static class Entry {
11 | Map stuff = new LinkedHashMap();
12 |
13 | public int age;
14 | public String name;
15 |
16 | @JsonAnySetter
17 | public void set(String key, Object value) {
18 | // for secondary test, where name remains empty:
19 | if (key.isEmpty()) {
20 | key = String.valueOf(stuff.size());
21 | }
22 | stuff.put(key, value);
23 | }
24 | }
25 |
26 | /*
27 | /**********************************************************************
28 | /* Test methods
29 | /**********************************************************************
30 | */
31 |
32 | public void testSimpleHeader() throws Exception
33 | {
34 | CsvMapper mapper = mapperForCsv();
35 | CsvSchema schema = CsvSchema.emptySchema().withHeader();
36 | Entry entry = mapper.readerFor(Entry.class).with(schema).readValue(
37 | "name,age,gender,extra\nBarbara,35,F,1246\n");
38 | assertEquals(35, entry.age);
39 | assertEquals("F", entry.stuff.get("gender"));
40 | assertEquals("1246", entry.stuff.get("extra"));
41 | assertEquals(2, entry.stuff.size());
42 | }
43 |
44 | // [dataformat-csv@109]: allow "any-setter-like"
45 | public void testWithMapToAny() throws Exception
46 | {
47 | CsvMapper mapper = mapperForCsv();
48 | CsvSchema schema = CsvSchema.emptySchema().withHeader()
49 | .withAnyPropertyName("");
50 | Entry entry = mapper.readerFor(Entry.class).with(schema)
51 | .readValue("name,age\nJoe,28,first,second\n");
52 | assertEquals("Joe", entry.name);
53 | assertEquals(28, entry.age);
54 | assertEquals("first", entry.stuff.get("0"));
55 | assertEquals("second", entry.stuff.get("1"));
56 | assertEquals(2, entry.stuff.size());
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/test/java/com/fasterxml/jackson/dataformat/csv/deser/ArrayReadTest.java:
--------------------------------------------------------------------------------
1 | package com.fasterxml.jackson.dataformat.csv.deser;
2 |
3 | import com.fasterxml.jackson.annotation.JsonCreator;
4 | import com.fasterxml.jackson.annotation.JsonProperty;
5 | import com.fasterxml.jackson.annotation.JsonPropertyOrder;
6 | import com.fasterxml.jackson.dataformat.csv.*;
7 |
8 | // for [dataformat-csv#57]
9 | public class ArrayReadTest extends ModuleTestBase
10 | {
11 | @JsonPropertyOrder({"id", "values", "extra"})
12 | static class ValueEntry {
13 | public String id, extra;
14 | public int[] values;
15 |
16 | @JsonCreator
17 | public ValueEntry(@JsonProperty("id") String id,
18 | @JsonProperty("extra") String extra,
19 | @JsonProperty("values") int[] values) {
20 | this.id = id;
21 | this.extra = extra;
22 | this.values = values;
23 | }
24 | }
25 |
26 | /*
27 | /**********************************************************************
28 | /* Test methods
29 | /**********************************************************************
30 | */
31 |
32 | private final CsvMapper MAPPER = mapperForCsv();
33 |
34 | public void testSimpleExplicitLooseTyping() throws Exception
35 | {
36 | ValueEntry value = MAPPER.readerWithSchemaFor(ValueEntry.class)
37 | .readValue("foo,1;2;3,stuff");
38 | assertNotNull(value);
39 | assertEquals("foo", value.id);
40 | assertEquals("stuff", value.extra);
41 | int[] v = value.values;
42 | assertNotNull(v);
43 | assertEquals(3, v.length);
44 | assertEquals(1, v[0]);
45 | assertEquals(2, v[1]);
46 | assertEquals(3, v[2]);
47 | }
48 |
49 | public void testSimpleExplicitStrictTyping() throws Exception
50 | {
51 | ValueEntry value = MAPPER.readerWithTypedSchemaFor(ValueEntry.class)
52 | .readValue("foo,1;2;3,stuff");
53 | assertNotNull(value);
54 | assertEquals("foo", value.id);
55 | assertEquals("stuff", value.extra);
56 | int[] v = value.values;
57 | assertNotNull(v);
58 | assertEquals(3, v.length);
59 | assertEquals(1, v[0]);
60 | assertEquals(2, v[1]);
61 | assertEquals(3, v[2]);
62 |
63 | // one more thing: for [dataformat-csv#66]:
64 | value = MAPPER.readerWithTypedSchemaFor(ValueEntry.class)
65 | .readValue("foo,,stuff");
66 | assertNotNull(value);
67 | assertEquals("foo", value.id);
68 | assertEquals("stuff", value.extra);
69 | v = value.values;
70 | assertNotNull(v);
71 | assertEquals(0, v.length);
72 | }
73 |
74 | public void testSeparatorOverrideSpace() throws Exception
75 | {
76 | ValueEntry input = new ValueEntry("foo", "stuff", new int[] {1, 2, 3});
77 | String csv = MAPPER.writer(CsvSchema.builder()
78 | .addColumn("id")
79 | .addArrayColumn("values", " ")
80 | .addColumn("extra")
81 | .build())
82 | .writeValueAsString(input)
83 | .trim();
84 | // gets quoted due to white space
85 | assertEquals("foo,\"1 2 3\",stuff", csv);
86 |
87 | ValueEntry value = MAPPER.reader(MAPPER.schemaFor(ValueEntry.class).withArrayElementSeparator(" ")).forType(ValueEntry.class)
88 | .readValue(csv);
89 | assertEquals("foo", value.id);
90 | assertEquals("stuff", value.extra);
91 | int[] v = value.values;
92 | assertNotNull(v);
93 | assertEquals(3, v.length);
94 | assertEquals(1, v[0]);
95 | assertEquals(2, v[1]);
96 | assertEquals(3, v[2]);
97 | }
98 |
99 | public void testSeparatorOverrideMulti() throws Exception
100 | {
101 | ValueEntry input = new ValueEntry("foo", "stuff", new int[] {1, 2, 3});
102 | String csv = MAPPER.writer(CsvSchema.builder()
103 | .addColumn("id")
104 | .addArrayColumn("values", "::")
105 | .addColumn("extra")
106 | .build())
107 | .writeValueAsString(input)
108 | .trim();
109 | assertEquals("foo,1::2::3,stuff", csv);
110 |
111 | ValueEntry value = MAPPER.reader(MAPPER.schemaFor(ValueEntry.class).withArrayElementSeparator("::")).forType(ValueEntry.class)
112 | .readValue(csv);
113 | assertEquals("foo", value.id);
114 | assertEquals("stuff", value.extra);
115 | int[] v = value.values;
116 | assertNotNull(v);
117 | assertEquals(3, v.length);
118 | assertEquals(1, v[0]);
119 | assertEquals(2, v[1]);
120 | assertEquals(3, v[2]);
121 | }
122 |
123 |
124 | }
125 |
--------------------------------------------------------------------------------
/src/test/java/com/fasterxml/jackson/dataformat/csv/deser/BrokenEncodingTest.java:
--------------------------------------------------------------------------------
1 | package com.fasterxml.jackson.dataformat.csv.deser;
2 |
3 | import java.io.ByteArrayOutputStream;
4 | import java.io.CharConversionException;
5 |
6 | import com.fasterxml.jackson.core.JsonToken;
7 | import com.fasterxml.jackson.dataformat.csv.*;
8 |
9 | public class BrokenEncodingTest extends ModuleTestBase
10 | {
11 | /*
12 | /**********************************************************************
13 | /* Test methods
14 | /**********************************************************************
15 | */
16 |
17 | // Simple test where a Latin-1 character is encountered; first byte wrong
18 | public void testLatin1AsUTF8() throws Exception
19 | {
20 | CsvFactory factory = new CsvFactory();
21 | String CSV = "1,2\nabc,\u00A0\n";
22 |
23 | CsvSchema schema = CsvSchema.builder()
24 | .addColumn("a")
25 | .addColumn("b")
26 | .build();
27 | // So: take Latin-1 bytes, but construct without specifying to lead to UTF-8 handling
28 | CsvParser parser = factory.createParser(CSV.getBytes("ISO-8859-1"));
29 | parser.setSchema(schema);
30 |
31 | assertToken(JsonToken.START_OBJECT, parser.nextToken());
32 | assertToken(JsonToken.FIELD_NAME, parser.nextToken());
33 | assertEquals("a", parser.getCurrentName());
34 | assertToken(JsonToken.VALUE_STRING, parser.nextToken());
35 | assertToken(JsonToken.FIELD_NAME, parser.nextToken());
36 | assertToken(JsonToken.VALUE_STRING, parser.nextToken());
37 | assertEquals("2", parser.getText());
38 | assertToken(JsonToken.END_OBJECT, parser.nextToken());
39 |
40 | // problem should only be triggered now
41 | assertToken(JsonToken.START_OBJECT, parser.nextToken());
42 | assertToken(JsonToken.FIELD_NAME, parser.nextToken());
43 | assertEquals("a", parser.getCurrentName());
44 | assertToken(JsonToken.VALUE_STRING, parser.nextToken());
45 | assertEquals("abc", parser.getText());
46 | try {
47 | parser.nextToken();
48 | fail("Should trigger exception for invalid UTF-8 char");
49 | } catch (CharConversionException e) {
50 | verifyException(e, "Invalid UTF-8 start byte");
51 | verifyException(e, "0xA0");
52 | }
53 | parser.close();
54 | }
55 |
56 | // Then a test with "middle" byte broken
57 | public void testBrokenUTF8MiddleByte() throws Exception
58 | {
59 | CsvFactory factory = new CsvFactory();
60 | ByteArrayOutputStream bytes = new ByteArrayOutputStream();
61 | bytes.write('1');
62 | bytes.write(',');
63 | bytes.write(0xD7); // this is fine
64 | bytes.write(0x41); // this not
65 | bytes.write('\n');
66 | CsvSchema schema = CsvSchema.builder()
67 | .addColumn("a")
68 | .addColumn("b")
69 | .build();
70 | // So: take Latin-1 bytes, but construct without specifying to lead to UTF-8 handling
71 | CsvParser parser = factory.createParser(bytes.toByteArray());
72 | parser.setSchema(schema);
73 |
74 | assertToken(JsonToken.START_OBJECT, parser.nextToken());
75 | assertToken(JsonToken.FIELD_NAME, parser.nextToken());
76 | assertEquals("a", parser.getCurrentName());
77 | assertToken(JsonToken.VALUE_STRING, parser.nextToken());
78 | assertEquals("1", parser.getText());
79 | try {
80 | assertToken(JsonToken.FIELD_NAME, parser.nextToken());
81 | assertEquals("b", parser.getCurrentName());
82 | parser.nextToken();
83 | fail("Should trigger exception for invalid UTF-8 char");
84 | } catch (CharConversionException e) {
85 | verifyException(e, "Invalid UTF-8 middle byte");
86 | verifyException(e, "0x41");
87 | }
88 | parser.close();
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/test/java/com/fasterxml/jackson/dataformat/csv/deser/CommentsTest.java:
--------------------------------------------------------------------------------
1 | package com.fasterxml.jackson.dataformat.csv.deser;
2 |
3 | import java.util.Map;
4 |
5 | import com.fasterxml.jackson.core.JsonParser;
6 | import com.fasterxml.jackson.databind.MappingIterator;
7 | import com.fasterxml.jackson.dataformat.csv.*;
8 |
9 | // Tests for [csv#56]
10 | public class CommentsTest extends ModuleTestBase
11 | {
12 | final String CSV_WITH_COMMENTS = "x,y\n# comment!\na,b\n# another...\n";
13 |
14 | public void testWithoutComments() throws Exception
15 | {
16 | CsvMapper mapper = mapperForCsv();
17 |
18 | // to handle comments that follow leading spaces
19 | mapper.enable(CsvParser.Feature.TRIM_SPACES);
20 | // should not be needed but seems to be...
21 | mapper.enable(CsvParser.Feature.WRAP_AS_ARRAY);
22 |
23 | String[] row;
24 |
25 | // First, with comments disabled:
26 |
27 | MappingIterator it = mapper.readerFor(String[].class)
28 | .with(mapper.schema().withoutComments()).readValues(CSV_WITH_COMMENTS);
29 |
30 | row = it.nextValue();
31 | assertEquals(2, row.length);
32 | assertEquals("x", row[0]);
33 | assertEquals("y", row[1]);
34 |
35 | // next, comment visible
36 | assertTrue(it.hasNext());
37 | row = it.nextValue();
38 | assertEquals("# comment!", row[0]);
39 | assertEquals(1, row.length);
40 |
41 | assertTrue(it.hasNext());
42 | row = it.nextValue();
43 | assertEquals(2, row.length);
44 | assertEquals("a", row[0]);
45 | assertEquals("b", row[1]);
46 |
47 | assertTrue(it.hasNext());
48 | row = it.nextValue();
49 | assertEquals("# another...", row[0]);
50 | assertEquals(1, row.length);
51 |
52 | assertFalse(it.hasNext());
53 | it.close();
54 | }
55 |
56 | public void testSimpleComments() throws Exception
57 | {
58 | CsvMapper mapper = mapperForCsv();
59 | // to handle comments that follow leading spaces
60 | mapper.enable(CsvParser.Feature.TRIM_SPACES);
61 | // should not be needed but seems to be...
62 | mapper.enable(CsvParser.Feature.WRAP_AS_ARRAY);
63 |
64 | MappingIterator it = mapper.readerFor(String[].class)
65 | .with(mapper.schema().withComments()).readValues(CSV_WITH_COMMENTS);
66 |
67 | // first row the same
68 | String[] row = it.nextValue();
69 | assertEquals(2, row.length);
70 | assertEquals("x", row[0]);
71 | assertEquals("y", row[1]);
72 |
73 | // next, comment NOT visible
74 | assertTrue(it.hasNext());
75 | row = it.nextValue();
76 | assertEquals("a", row[0]);
77 | assertEquals(2, row.length);
78 | assertEquals("b", row[1]);
79 |
80 | // and ditto for second comment
81 | assertFalse(it.hasNext());
82 | it.close();
83 | }
84 |
85 | public void testLeadingComments() throws Exception
86 | {
87 | CsvMapper mapper = mapperForCsv();
88 | // should not be needed but seems to be...
89 | mapper.enable(CsvParser.Feature.WRAP_AS_ARRAY);
90 |
91 | MappingIterator it = mapper.readerFor(String[].class)
92 | .with(mapper.schema().withComments()).readValues("# first\n#second\n1,2\n");
93 |
94 | // first row the same
95 | String[] row = it.nextValue();
96 | assertEquals("1", row[0]);
97 | assertEquals(2, row.length);
98 | assertEquals("2", row[1]);
99 |
100 | assertFalse(it.hasNextValue());
101 |
102 | it.close();
103 | }
104 |
105 | public void testCommentsWithHeaderRow() throws Exception
106 | {
107 | CsvMapper mapper = mapperForCsv();
108 | MappingIterator