Skip to content

Commit fb116d3

Browse files
authored
Merge pull request #4 from AnetteTaivere/GobPieConfTests
Tests related to GobPie configuration
2 parents 6aaa45d + 317a0cc commit fb116d3

File tree

10 files changed

+469
-37
lines changed

10 files changed

+469
-37
lines changed
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package api.json;
2+
3+
import com.google.gson.Gson;
4+
import com.google.gson.JsonParseException;
5+
import com.google.gson.TypeAdapter;
6+
import com.google.gson.TypeAdapterFactory;
7+
import com.google.gson.internal.bind.ReflectiveTypeAdapterFactory;
8+
import com.google.gson.reflect.TypeToken;
9+
10+
import java.lang.reflect.Field;
11+
import java.util.LinkedHashMap;
12+
import java.util.Map;
13+
14+
/**
15+
* A class for handling unexpected fields in GobPie configuration.
16+
* Code adapted from <a href="https://github.com/google/gson/issues/188">a Gson library issue</a>.
17+
*
18+
* @since 0.0.4
19+
*/
20+
21+
public class GobPieConfValidatorAdapterFactory implements TypeAdapterFactory {
22+
23+
@Override
24+
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
25+
// If the type adapter is a reflective type adapter, we want to modify the implementation using reflection.
26+
// The trick is to replace the Map object used to look up the property name.
27+
// Instead of returning null if the property is not found,
28+
// we throw a Json exception to terminate the deserialization.
29+
TypeAdapter<T> delegateAdapter = gson.getDelegateAdapter(this, type);
30+
31+
// Check if the type adapter is a reflective, cause this solution only work for reflection.
32+
if (delegateAdapter instanceof ReflectiveTypeAdapterFactory.Adapter) {
33+
34+
try {
35+
// Get reference to the existing boundFields.
36+
Field f = findBoundField(delegateAdapter.getClass());
37+
f.setAccessible(true);
38+
// Finally, push our custom map back using reflection.
39+
f.set(delegateAdapter, getBoundFields(f, delegateAdapter));
40+
} catch (Exception e) {
41+
// Should never happen if the implementation doesn't change.
42+
throw new IllegalStateException(e);
43+
}
44+
45+
}
46+
return delegateAdapter;
47+
}
48+
49+
@SuppressWarnings("unchecked")
50+
private static <T> Object getBoundFields(Field f, TypeAdapter<T> delegate) throws IllegalAccessException {
51+
Object boundFields = f.get(delegate);
52+
53+
// Then replace it with our implementation throwing exception if the value is null.
54+
boundFields = new LinkedHashMap<>((Map<Object, Object>) boundFields) {
55+
56+
@Override
57+
public Object get(Object key) {
58+
Object value = super.get(key);
59+
if (value == null) {
60+
throw new JsonParseException(String.valueOf(key));
61+
}
62+
return value;
63+
}
64+
65+
};
66+
return boundFields;
67+
}
68+
69+
private static Field findBoundField(Class<?> startingClass) throws NoSuchFieldException {
70+
for (Class<?> c = startingClass; c != null; c = c.getSuperclass()) {
71+
try {
72+
return c.getDeclaredField("boundFields");
73+
} catch (NoSuchFieldException e) {
74+
// OK: continue with superclasses
75+
}
76+
}
77+
throw new NoSuchFieldException("boundFields starting from " + (startingClass != null ? startingClass.getName() : null));
78+
}
79+
}

src/main/java/gobpie/GobPieConfReader.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package gobpie;
22

3+
import api.json.GobPieConfValidatorAdapterFactory;
34
import com.google.gson.*;
45
import magpiebridge.core.MagpieServer;
56
import org.apache.commons.lang3.exception.ExceptionUtils;
@@ -28,7 +29,7 @@ public class GobPieConfReader {
2829

2930
private final MagpieServer magpieServer;
3031
private final String gobPieConfFileName;
31-
private static final Logger log = LogManager.getLogger(GobPieConfReader.class);
32+
private final Logger log = LogManager.getLogger(GobPieConfReader.class);
3233

3334
public GobPieConfReader(MagpieServer magpieServer, String gobPieConfFileName) {
3435
this.magpieServer = magpieServer;
@@ -93,21 +94,25 @@ public GobPieConfiguration readGobPieConfiguration() {
9394
public GobPieConfiguration parseGobPieConf() {
9495
try {
9596
log.debug("Reading GobPie configuration from json");
96-
Gson gson = new GsonBuilder().create();
97+
Gson gson = new GsonBuilder()
98+
.registerTypeAdapterFactory(new GobPieConfValidatorAdapterFactory())
99+
.create();
97100
// Read json object
98101
JsonObject jsonObject = JsonParser.parseReader(new FileReader(gobPieConfFileName)).getAsJsonObject();
99102
// Convert json object to GobPieConfiguration object
100103
log.debug("GobPie configuration read from json");
101104
return gson.fromJson(jsonObject, GobPieConfiguration.class);
102-
} catch (FileNotFoundException e) {
103-
throw new GobPieException("Could not locate GobPie configuration file.", e, GobPieExceptionType.GOBPIE_CONF_EXCEPTION);
104105
} catch (JsonSyntaxException e) {
105106
throw new GobPieException("GobPie configuration file syntax is wrong.", e, GobPieExceptionType.GOBPIE_CONF_EXCEPTION);
107+
} catch (JsonParseException e) {
108+
throw new GobPieException("There was an unknown option \"" + e.getMessage() + "\" in the GobPie configuration. Please check for any typos.", e, GobPieExceptionType.GOBPIE_CONF_EXCEPTION);
109+
} catch (FileNotFoundException e) {
110+
throw new GobPieException("Could not locate GobPie configuration file.", e, GobPieExceptionType.GOBPIE_CONF_EXCEPTION);
106111
}
107112
}
108113

109114

110-
/**
115+
/**
111116
* Method for forwarding Error messages to MagpieServer.
112117
*
113118
* @param popUpMessage The message shown on the pop-up message.

src/main/java/gobpie/GobPieConfiguration.java

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package gobpie;
22

3+
import java.util.Arrays;
4+
import java.util.Objects;
5+
36
/**
47
* The Class GobPieConfiguration.
58
* <p>
@@ -26,6 +29,16 @@ private GobPieConfiguration() {
2629
incrementalAnalysis = true;
2730
}
2831

32+
private GobPieConfiguration(String goblintExecutable, String goblintConf, String[] preAnalyzeCommand, boolean abstractDebugging, boolean showCfg, boolean explodeGroupWarnings, boolean incrementalAnalysis) {
33+
this.goblintExecutable = goblintExecutable;
34+
this.goblintConf = goblintConf;
35+
this.preAnalyzeCommand = preAnalyzeCommand;
36+
this.abstractDebugging = abstractDebugging;
37+
this.showCfg = showCfg;
38+
this.explodeGroupWarnings = explodeGroupWarnings;
39+
this.incrementalAnalysis = incrementalAnalysis;
40+
}
41+
2942
public String getGoblintExecutable() {
3043
return this.goblintExecutable;
3144
}
@@ -54,4 +67,80 @@ public boolean useIncrementalAnalysis() {
5467
return incrementalAnalysis;
5568
}
5669

70+
@Override
71+
public boolean equals(Object o) {
72+
if (this == o) return true;
73+
if (o == null || getClass() != o.getClass()) return false;
74+
GobPieConfiguration that = (GobPieConfiguration) o;
75+
return abstractDebugging == that.abstractDebugging && showCfg == that.showCfg && explodeGroupWarnings == that.explodeGroupWarnings && incrementalAnalysis == that.incrementalAnalysis && Objects.equals(goblintExecutable, that.goblintExecutable) && Objects.equals(goblintConf, that.goblintConf) && Arrays.equals(preAnalyzeCommand, that.preAnalyzeCommand);
76+
}
77+
78+
@Override
79+
public int hashCode() {
80+
int result = Objects.hash(goblintExecutable, goblintConf, abstractDebugging, showCfg, explodeGroupWarnings, incrementalAnalysis);
81+
result = 31 * result + Arrays.hashCode(preAnalyzeCommand);
82+
return result;
83+
}
84+
85+
@Override
86+
public String toString() {
87+
return "GobPieConfiguration{" +
88+
"goblintExecutable='" + goblintExecutable + '\'' +
89+
", goblintConf='" + goblintConf + '\'' +
90+
", preAnalyzeCommand=" + Arrays.toString(preAnalyzeCommand) +
91+
", abstractDebugging=" + abstractDebugging +
92+
", showCfg=" + showCfg +
93+
", explodeGroupWarnings=" + explodeGroupWarnings +
94+
", incrementalAnalysis=" + incrementalAnalysis +
95+
'}';
96+
}
97+
98+
public static class Builder {
99+
private String goblintExecutable;
100+
private String goblintConf;
101+
private String[] preAnalyzeCommand;
102+
private boolean abstractDebugging;
103+
private boolean showCfg;
104+
private boolean explodeGroupWarnings;
105+
private boolean incrementalAnalysis;
106+
107+
public Builder setGoblintExecutable(String goblintExecutable) {
108+
this.goblintExecutable = goblintExecutable;
109+
return this;
110+
}
111+
112+
public Builder setGoblintConf(String goblintConf) {
113+
this.goblintConf = goblintConf;
114+
return this;
115+
}
116+
117+
public Builder setPreAnalyzeCommand(String[] preAnalyzeCommand) {
118+
this.preAnalyzeCommand = preAnalyzeCommand;
119+
return this;
120+
}
121+
122+
public Builder setAbstractDebugging(boolean abstractDebugging) {
123+
this.abstractDebugging = abstractDebugging;
124+
return this;
125+
}
126+
127+
public Builder setShowCfg(boolean showCfg) {
128+
this.showCfg = showCfg;
129+
return this;
130+
}
131+
132+
public Builder setExplodeGroupWarnings(boolean explodeGroupWarnings) {
133+
this.explodeGroupWarnings = explodeGroupWarnings;
134+
return this;
135+
}
136+
137+
public Builder setIncrementalAnalysis(boolean incrementalAnalysis) {
138+
this.incrementalAnalysis = incrementalAnalysis;
139+
return this;
140+
}
141+
142+
public GobPieConfiguration createGobPieConfiguration() {
143+
return new GobPieConfiguration(goblintExecutable, goblintConf, preAnalyzeCommand, abstractDebugging, showCfg, explodeGroupWarnings, incrementalAnalysis);
144+
}
145+
}
57146
}

0 commit comments

Comments
 (0)