Skip to content

Commit 81e28ec

Browse files
Craigacpjhalexand
authored andcommitted
Adding ObjectProvenance.maybeExtractProvenance to aid with evolving provenance classes. Refactored the tests to test both the new method and ObjectProvenance.checkAndExtractProvenance.
1 parent f376793 commit 81e28ec

File tree

2 files changed

+45
-18
lines changed

2 files changed

+45
-18
lines changed

olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/provenance/ObjectProvenance.java

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import com.oracle.labs.mlrg.olcut.util.Pair;
3333

3434
import java.util.Map;
35+
import java.util.Optional;
3536

3637
/**
3738
* A provenance object which records object fields.
@@ -83,7 +84,7 @@ default public String generateString(String name) {
8384
/**
8485
* Removes the specified Provenance from the supplied map and returns it. Checks that it's the right type,
8586
* and casts to it before returning.
86-
*
87+
* <p>
8788
* Throws ProvenanceException if it's not found or it's an incorrect type.
8889
* @param map The map to check.
8990
* @param key The key to look up.
@@ -95,15 +96,40 @@ default public String generateString(String name) {
9596
*/
9697
@SuppressWarnings("unchecked") // Guarded by isInstance check
9798
public static <T extends Provenance> T checkAndExtractProvenance(Map<String,Provenance> map, String key, Class<T> type, String provClassName) throws ProvenanceException {
99+
Optional<T> prov = maybeExtractProvenance(map,key,type,provClassName);
100+
if (prov.isPresent()) {
101+
return prov.get();
102+
} else {
103+
throw new ProvenanceException("Failed to find " + key + " when constructing " + provClassName);
104+
}
105+
}
106+
107+
/**
108+
* Removes the specified Provenance from the supplied map and returns it. Checks that it's the right type,
109+
* and casts to it before returning. Unlike {@link #checkAndExtractProvenance(Map, String, Class, String)} it doesn't
110+
* throw if it fails to find the key, only if the value is of the wrong type.
111+
* <p>
112+
* This is used when evolving provenance classes by adding new fields to ensure that old serialized
113+
* forms remain compatible.
114+
* @param map The map to inspect.
115+
* @param key The key to find.
116+
* @param type The class of the value.
117+
* @param provClassName The name of the requesting class (to ensure the exception has the appropriate error message).
118+
* @param <T> The type of the value.
119+
* @return An optional containing the value if present.
120+
* @throws ProvenanceException If the value is the wrong type.
121+
*/
122+
@SuppressWarnings("unchecked") // Guarded by isInstance check
123+
public static <T extends Provenance> Optional<T> maybeExtractProvenance(Map<String,Provenance> map, String key, Class<T> type, String provClassName) throws ProvenanceException {
98124
Provenance tmp = map.remove(key);
99125
if (tmp != null) {
100126
if (type.isInstance(tmp)) {
101-
return (T) tmp;
127+
return Optional.of((T) tmp);
102128
} else {
103129
throw new ProvenanceException("Failed to cast " + key + " when constructing " + provClassName + ", found " + tmp);
104130
}
105131
} else {
106-
throw new ProvenanceException("Failed to find " + key + " when constructing " + provClassName);
132+
return Optional.empty();
107133
}
108134
}
109135
}

olcut-core/src/test/java/com/oracle/labs/mlrg/olcut/provenance/ExampleProvenancableConfigurable.java

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@
4141
import java.util.List;
4242
import java.util.Map;
4343
import java.util.Objects;
44+
import java.util.Optional;
45+
46+
import static org.junit.jupiter.api.Assertions.fail;
4447

4548
/**
4649
* Test class for the provenance system.
@@ -122,17 +125,20 @@ public ExampleProvenance(ExampleProvenancableConfigurable host) {
122125

123126
@SuppressWarnings("unchecked")
124127
public ExampleProvenance(Map<String,Provenance> provenances) {
128+
this.className = ObjectProvenance.checkAndExtractProvenance(provenances,ObjectProvenance.CLASS_NAME,StringProvenance.class,ExampleProvenance.class.getName()).getValue();
129+
this.map = ObjectProvenance.checkAndExtractProvenance(provenances,MAP,MapProvenance.class,ExampleProvenance.class.getName());
130+
Optional<DoubleProvenance> opt = ObjectProvenance.maybeExtractProvenance(provenances,DOUBLE_FIELD,DoubleProvenance.class,ExampleProvenance.class.getName());
131+
if (opt.isPresent()) {
132+
this.doubleField = opt.get();
133+
} else {
134+
throw new ProvenanceException("Failed to find " + DOUBLE_FIELD + " when constructing ExampleProvenance");
135+
}
136+
Optional<DoubleProvenance> notPresentOpt = ObjectProvenance.maybeExtractProvenance(provenances,"DEFINITELY-NOT-HERE",DoubleProvenance.class,ExampleProvenance.class.getName());
137+
if (notPresentOpt.isPresent()) {
138+
fail("Found a provenance which wasn't there");
139+
}
140+
125141
try {
126-
if (provenances.containsKey(ObjectProvenance.CLASS_NAME)) {
127-
this.className = provenances.get(ObjectProvenance.CLASS_NAME).toString();
128-
} else {
129-
throw new ProvenanceException("Failed to find class name when constructing ExampleProvenance");
130-
}
131-
if (provenances.containsKey(DOUBLE_FIELD)) {
132-
this.doubleField = (DoubleProvenance) provenances.get(DOUBLE_FIELD);
133-
} else {
134-
throw new ProvenanceException("Failed to find " + DOUBLE_FIELD + " when constructing ExampleProvenance");
135-
}
136142
if (provenances.containsKey(INT_ARRAY_FIELD)) {
137143
this.intArrayField = (ListProvenance<IntProvenance>) provenances.get(INT_ARRAY_FIELD);
138144
} else {
@@ -143,11 +149,6 @@ public ExampleProvenance(Map<String,Provenance> provenances) {
143149
} else {
144150
throw new ProvenanceException("Failed to find " + EXAMPLES + " when constructing ExampleProvenance");
145151
}
146-
if (provenances.containsKey(MAP)) {
147-
this.map = (MapProvenance<StringProvenance>) provenances.get(MAP);
148-
} else {
149-
throw new ProvenanceException("Failed to find " + MAP + " when constructing ExampleProvenance");
150-
}
151152
} catch (ClassCastException e) {
152153
throw new ProvenanceException("Incorrect type found in provenance, did not match the field type.",e);
153154
}

0 commit comments

Comments
 (0)