Skip to content

Commit fb850e7

Browse files
authored
CSV: Use system identity-based hashcodes (fixes #180) (#185)
1 parent afdc104 commit fb850e7

File tree

2 files changed

+60
-11
lines changed
  • plugins/org.eclipse.epsilon.emc.csv/src/org/eclipse/epsilon/emc/csv
  • tests/org.eclipse.epsilon.emc.csv.test/src/org/eclipse/epsilon/emc/csv

2 files changed

+60
-11
lines changed

plugins/org.eclipse.epsilon.emc.csv/src/org/eclipse/epsilon/emc/csv/CsvModel.java

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,26 @@
1616
import java.nio.charset.Charset;
1717
import java.nio.file.Files;
1818
import java.nio.file.Paths;
19-
import java.util.*;
19+
import java.util.ArrayList;
20+
import java.util.Collection;
21+
import java.util.Collections;
22+
import java.util.Iterator;
23+
import java.util.LinkedHashMap;
24+
import java.util.LinkedList;
25+
import java.util.List;
26+
import java.util.Map;
27+
import java.util.Set;
28+
2029
import org.apache.commons.csv.CSVFormat;
2130
import org.apache.commons.csv.CSVParser;
2231
import org.apache.commons.csv.CSVRecord;
2332
import org.eclipse.epsilon.common.util.FileUtil;
2433
import org.eclipse.epsilon.common.util.StringProperties;
2534
import org.eclipse.epsilon.eol.exceptions.EolRuntimeException;
26-
import org.eclipse.epsilon.eol.exceptions.models.*;
35+
import org.eclipse.epsilon.eol.exceptions.models.EolEnumerationValueNotFoundException;
36+
import org.eclipse.epsilon.eol.exceptions.models.EolModelElementTypeNotFoundException;
37+
import org.eclipse.epsilon.eol.exceptions.models.EolModelLoadingException;
38+
import org.eclipse.epsilon.eol.exceptions.models.EolNotInstantiableModelElementTypeException;
2739
import org.eclipse.epsilon.eol.models.CachedModel;
2840
import org.eclipse.epsilon.eol.models.IRelativePathResolver;
2941

@@ -58,6 +70,26 @@
5870
*/
5971
public class CsvModel extends CachedModel<Map<String, Object>> {
6072

73+
/**
74+
* Various Epsilon languages assume that different model
75+
* elements will have different hashcodes (e.g. ETL). This
76+
* subclass of LinkedHashMap reverts to system identity-based
77+
* hashcodes, to ensure that.
78+
*/
79+
protected static class Row extends LinkedHashMap<String, Object> {
80+
private static final long serialVersionUID = 1L;
81+
82+
@Override
83+
public int hashCode() {
84+
return System.identityHashCode(this);
85+
}
86+
87+
@Override
88+
public boolean equals(Object other) {
89+
return this == other;
90+
}
91+
}
92+
6193
public static final String HEADERLESS_FIELD_NAME = "field";
6294

6395
/** The Constant PROPERTY_FILE. */
@@ -357,7 +389,7 @@ private String concatenateMap() {
357389
StringBuilder output = new StringBuilder();
358390
if (this.knownHeaders) {
359391
// First line is the headers
360-
Iterator<String> keyIt = ((LinkedList<Map<String, Object>>) rows).getFirst().keySet().iterator();
392+
Iterator<String> keyIt = rows.get(0).keySet().iterator();
361393
output.append(keyIt.next());
362394
while (keyIt.hasNext()) {
363395
output.append(this.fieldSeparator);
@@ -412,12 +444,13 @@ protected Collection<Map<String, Object>> getAllOfKindFromModel(String kind) thr
412444
* @see org.eclipse.epsilon.eol.models.CachedModel#createInstanceInModel(java.lang.String)
413445
*/
414446
@Override
415-
protected Map<String, Object> createInstanceInModel(String type) throws EolModelElementTypeNotFoundException, EolNotInstantiableModelElementTypeException {
447+
protected Row createInstanceInModel(String type) throws EolModelElementTypeNotFoundException, EolNotInstantiableModelElementTypeException {
416448
if (!"Row".equals(type)) {
417449
throw new EolModelElementTypeNotFoundException(this.name, type);
418450
}
419-
Map<String, Object> returnVal = new LinkedHashMap<>();
420-
for (String key : ((LinkedList<Map<String, Object>>) rows).getFirst().keySet()) {
451+
452+
Row returnVal = new Row();
453+
for (String key : rows.get(0).keySet()) {
421454
returnVal.put(key, "");
422455
}
423456
rows.add(returnVal);
@@ -440,7 +473,7 @@ public boolean hasProperty(String type, String property) throws EolModelElementT
440473
if (!this.knownHeaders) {
441474
return property.equals(HEADERLESS_FIELD_NAME);
442475
} else {
443-
return ((LinkedHashMap<String, Object>) ((LinkedList<Map<String, Object>>) rows).getFirst()).keySet().contains(property);
476+
return rows.get(0).keySet().contains(property);
444477
}
445478
}
446479

@@ -471,7 +504,7 @@ protected static List<Map<String, Object>> createRows(BufferedReader reader,
471504
try (CSVParser records = csvFormat.parse(reader)) {
472505
if (knownHeaders) {
473506
for (CSVRecord record : records) {
474-
LinkedHashMap<String, Object> row = new LinkedHashMap<>();
507+
Row row = new Row();
475508
if (!varargsHeaders) {
476509
for (Map.Entry<String, String> entry : record.toMap().entrySet()) {
477510
row.put(entry.getKey(), entry.getValue());
@@ -502,7 +535,7 @@ protected static List<Map<String, Object>> createRows(BufferedReader reader,
502535
for (CSVRecord record : records) {
503536
List<String> values = new ArrayList<>();
504537
record.iterator().forEachRemaining(values::add);
505-
LinkedHashMap<String, Object> row = new LinkedHashMap<>();
538+
Row row = new Row();
506539
row.put("field", values);
507540
rows.add(row);
508541
}

tests/org.eclipse.epsilon.emc.csv.test/src/org/eclipse/epsilon/emc/csv/CsvModelTests.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@
1111

1212
import static org.junit.Assert.assertEquals;
1313
import static org.junit.Assert.assertFalse;
14+
import static org.junit.Assert.assertNotEquals;
1415
import static org.junit.Assert.assertNull;
1516
import static org.junit.Assert.assertTrue;
1617
import static org.junit.Assert.fail;
18+
1719
import java.io.BufferedReader;
1820
import java.io.StringReader;
1921
import java.util.ArrayList;
@@ -25,8 +27,8 @@
2527
import java.util.List;
2628
import java.util.Map;
2729
import java.util.Random;
30+
2831
import org.eclipse.epsilon.eol.exceptions.models.EolModelElementTypeNotFoundException;
29-
import org.eclipse.epsilon.eol.exceptions.models.EolNotInstantiableModelElementTypeException;
3032
import org.junit.After;
3133
import org.junit.Before;
3234
import org.junit.Rule;
@@ -373,7 +375,7 @@ public void testGetAllOfKindFromModelString() throws Exception {
373375
}
374376

375377
@Test
376-
public void testCreateInstanceInModelString() throws Exception, EolNotInstantiableModelElementTypeException {
378+
public void testCreateInstanceInModelString() throws Exception {
377379
Map<String, Object> newRow = model.createInstanceInModel("Row");
378380
for (String key : headers) {
379381
assertTrue(String.format("Field %s should be empty", key), ((String) newRow.get(key)).isEmpty());
@@ -382,6 +384,20 @@ public void testCreateInstanceInModelString() throws Exception, EolNotInstantiab
382384
newRow = model.createInstanceInModel("TypeA");
383385
}
384386

387+
@Test
388+
public void testDifferentRowsHaveDifferentHashcodes() throws Exception {
389+
Map<String, Object> newRow1 = model.createInstanceInModel("Row");
390+
Map<String, Object> newRow2 = model.createInstanceInModel("Row");
391+
392+
/*
393+
* Different rows should have different hashcodes and be considered not to be
394+
* equal to each other. Otherwise, this may cause issues in some Epsilon
395+
* languages, such as ETL.
396+
*/
397+
assertNotEquals(newRow1.hashCode(), newRow2.hashCode());
398+
assertNotEquals(newRow1, newRow2);
399+
}
400+
385401
@Test
386402
public void testHasProperty() throws Exception {
387403
for (String key : headers) {

0 commit comments

Comments
 (0)