ExceptionThrower.java

1
package com.github.dakusui.symfonion.exceptions;
2
3
import com.github.dakusui.json.JsonUtils;
4
import com.github.dakusui.symfonion.utils.midi.MidiDeviceRecord;
5
import com.google.gson.JsonArray;
6
import com.google.gson.JsonElement;
7
import com.google.gson.JsonObject;
8
9
import javax.sound.midi.MidiDevice;
10
import javax.sound.midi.MidiUnavailableException;
11
import java.io.Closeable;
12
import java.io.File;
13
import java.util.HashMap;
14
import java.util.function.Predicate;
15
import java.util.regex.Matcher;
16
17
import static com.github.dakusui.symfonion.cli.CliUtils.composeErrMsg;
18
import static com.github.dakusui.symfonion.exceptions.ExceptionThrower.ContextKey.*;
19
import static com.github.dakusui.valid8j.ValidationFluents.all;
20
import static com.github.dakusui.valid8j.ValidationFluents.that;
21
import static com.github.dakusui.valid8j_pcond.fluent.Statement.objectValue;
22
import static java.lang.String.format;
23
24
public class ExceptionThrower {
25
  public enum ContextKey {
26
    MIDI_DEVICE_INFO(MidiDevice.Info.class),
27
    MIDI_DEVICE_INFO_IO(String.class),
28
    JSON_ELEMENT_ROOT(JsonObject.class),
29
    SOURCE_FILE(File.class);
30
    private final Class<?> type;
31
32
    ContextKey(Class<?> type) {
33
      this.type = type;
34
    }
35
  }
36
37
  public record ContextEntry(ContextKey key, Object value) {
38
  }
39
40
  public static class Context implements Closeable {
41
    private final Context parent;
42
    private final HashMap<ExceptionThrower.ContextKey, Object> values = HashMap.newHashMap(100);
43
44
    private Context(Context parent) {
45
      this.parent = parent;
46
    }
47
48
    public Context() {
49
      this.parent = null;
50
    }
51
52
    public Context set(ExceptionThrower.ContextKey key, Object value) {
53
      assert all(
54
          objectValue(key).then().isNotNull().$(),
55
          objectValue(value).then().isNotNull().isInstanceOf(key.type).$());
56
      this.values.put(key, value);
57 1 1. set : replaced return value with null for com/github/dakusui/symfonion/exceptions/ExceptionThrower$Context::set → SURVIVED
      return this;
58
    }
59
60
    public Context parent() {
61 1 1. parent : replaced return value with null for com/github/dakusui/symfonion/exceptions/ExceptionThrower$Context::parent → KILLED
      return this.parent;
62
    }
63
64
    /**
65
     *
66
     */
67
    @SuppressWarnings({"unchecked"})
68
    <T> T get(ContextKey key) {
69
      assert that(objectValue(key).then().isNotNull().$());
70
      // In production, we do not want to produce a NullPointerException, even if the key is null.
71
      // Just return null in such a situation.
72 1 1. get : negated conditional → KILLED
      if (key == null)
73
        return null;
74 1 1. get : negated conditional → KILLED
      if (!this.values.containsKey(key)) {
75
        assert that(objectValue(this).invoke("parent").then().isNotNull());
76
        // In production, we do not want to produce a NullPointerException, even if a value associated with the key doesn't exist.
77
        // Just return null, in such a situation.
78 1 1. get : negated conditional → KILLED
        if (this.parent() == null)
79
          return null;
80 1 1. get : replaced return value with null for com/github/dakusui/symfonion/exceptions/ExceptionThrower$Context::get → KILLED
        return this.parent().get(key);
81
      }
82 1 1. get : replaced return value with null for com/github/dakusui/symfonion/exceptions/ExceptionThrower$Context::get → KILLED
      return (T) this.values.get(key);
83
    }
84
85
86
    @Override
87
    public void close() {
88 1 1. close : removed call to java/lang/ThreadLocal::set → SURVIVED
      ExceptionThrower.context.set(this.parent);
89
    }
90
91
    Context createChild() {
92 1 1. createChild : replaced return value with null for com/github/dakusui/symfonion/exceptions/ExceptionThrower$Context::createChild → KILLED
      return new Context(this);
93
    }
94
  }
95
96
  private static final ThreadLocal<Context> context = new ThreadLocal<>();
97
98
  public static ContextEntry contextEntry(ContextKey key, Object value) {
99
    assert all(
100
        objectValue(key).then().isNotNull().$(),
101
        objectValue(value).then().isNotNull().isInstanceOf(classOfValueFor(key)).$());
102 1 1. contextEntry : replaced return value with null for com/github/dakusui/symfonion/exceptions/ExceptionThrower::contextEntry → KILLED
    return new ContextEntry(key, value);
103
  }
104
105
106
  public static ContextEntry $(ContextKey key, Object value) {
107 1 1. $ : replaced return value with null for com/github/dakusui/symfonion/exceptions/ExceptionThrower::$ → KILLED
    return contextEntry(key, value);
108
  }
109
110
  public static Context context(ContextEntry... entries) {
111
    Context ret = currentContext().createChild();
112 1 1. context : removed call to java/lang/ThreadLocal::set → KILLED
    context.set(ret);
113
    for (ContextEntry each : entries)
114
      ret.set(each.key(), each.value());
115 1 1. context : replaced return value with null for com/github/dakusui/symfonion/exceptions/ExceptionThrower::context → SURVIVED
    return ret;
116
  }
117
118
  public static SymfonionException compilationException(String msg, Throwable e) throws SymfonionException {
119
    throw new SymfonionException(msg, e, contextValueOf(SOURCE_FILE));
120
  }
121
122
  public static SymfonionException fileNotFoundException(File file, Throwable e) throws SymfonionException {
123
    throw new SymfonionException(format("%s: File not found (%s)", file, e.getMessage()), file);
124
  }
125
126
  public static SymfonionException loadFileException(Throwable e) throws SymfonionException {
127
    throw new SymfonionException(format("%s: %s", contextValueOf(SOURCE_FILE), e.getMessage()), contextValueOf(SOURCE_FILE));
128
  }
129
130
  public static SymfonionException loadResourceException(String resourceName, Throwable e) throws SymfonionException {
131
    throw new SymfonionException(format("%s: Failed to read resource (%s)", resourceName, e.getMessage()), contextValueOf(SOURCE_FILE));
132
  }
133
134
  public static SymfonionReferenceException noteMapNotFoundException(JsonElement problemCausingJsonNode, String missingReference) throws SymfonionException {
135
    throw new SymfonionReferenceException(missingReference, "notemap", problemCausingJsonNode, contextValueOf(JSON_ELEMENT_ROOT), contextValueOf(SOURCE_FILE), contextValueOf(JSON_ELEMENT_ROOT));
136
  }
137
138
  public static SymfonionReferenceException noteNotDefinedException(JsonElement problemCausingJsonNode, String missingReference, String notemapName) throws SymfonionException {
139
    throw new SymfonionReferenceException(missingReference, format("note in %s", notemapName), problemCausingJsonNode, contextValueOf(JSON_ELEMENT_ROOT), contextValueOf(SOURCE_FILE), contextValueOf(JSON_ELEMENT_ROOT));
140
  }
141
142
  public static SymfonionException syntaxErrorInNotePattern(String s, int i, Matcher m) {
143 1 1. syntaxErrorInNotePattern : replaced return value with null for com/github/dakusui/symfonion/exceptions/ExceptionThrower::syntaxErrorInNotePattern → NO_COVERAGE
    return new SymfonionException("Error:" + s.substring(0, i) + "[" + s.substring(i, m.start()) + "]" + s.substring(m.start()), contextValueOf(SOURCE_FILE));
144
  }
145
146
  public static SymfonionReferenceException grooveNotDefinedException(JsonElement problemCausingJsonNode, String missingReference) throws SymfonionException {
147
    throw new SymfonionReferenceException(missingReference, "groove", problemCausingJsonNode, contextValueOf(JSON_ELEMENT_ROOT), contextValueOf(SOURCE_FILE), JsonUtils.asJsonElement(contextValueOf(JSON_ELEMENT_ROOT), "$grooves"));
148
  }
149
150
  public static SymfonionReferenceException partNotFound(JsonElement problemCausingJsonNode, String missingReference) throws SymfonionException {
151
    throw new SymfonionReferenceException(missingReference, "part", problemCausingJsonNode, contextValueOf(JSON_ELEMENT_ROOT), contextValueOf(SOURCE_FILE), JsonUtils.asJsonElement(contextValueOf(JSON_ELEMENT_ROOT), "$parts"));
152
  }
153
154
  public static SymfonionReferenceException patternNotFound(JsonElement problemCausingJsonNode, String missingReference) throws SymfonionException {
155
    throw new SymfonionReferenceException(missingReference, "pattern", problemCausingJsonNode, contextValueOf(JSON_ELEMENT_ROOT), contextValueOf(SOURCE_FILE), JsonUtils.asJsonElement(contextValueOf(JSON_ELEMENT_ROOT), "$patterns"));
156
  }
157
158
  public static SymfonionTypeMismatchException typeMismatchException(JsonElement actualJSON, String... expectedTypes) throws SymfonionSyntaxException {
159
    throw new SymfonionTypeMismatchException(expectedTypes, actualJSON, actualJSON, contextValueOf(JSON_ELEMENT_ROOT), contextValueOf(SOURCE_FILE));
160
  }
161
162
  public static SymfonionIllegalFormatException illegalFormatException(JsonElement actualJSON, String acceptableExample) throws SymfonionIllegalFormatException {
163
    throw new SymfonionIllegalFormatException(actualJSON, acceptableExample, contextValueOf(JSON_ELEMENT_ROOT), contextValueOf(SOURCE_FILE));
164
  }
165
166
  public static SymfonionIllegalFormatException syntaxErrorWhenExpandingDotsIn(JsonArray errorContainingJsonArray) {
167 1 1. syntaxErrorWhenExpandingDotsIn : replaced return value with null for com/github/dakusui/symfonion/exceptions/ExceptionThrower::syntaxErrorWhenExpandingDotsIn → NO_COVERAGE
    return new SymfonionIllegalFormatException(
168
        errorContainingJsonArray,
169
        "In this array, a string can contain only dots. E.g. '[1, \"...\",3]'. This will be expanded and interpolation of integer values will happen.",
170
        contextValueOf(JSON_ELEMENT_ROOT),
171
        contextValueOf(SOURCE_FILE) );
172
  }
173
174
  public static SymfonionIllegalFormatException typeMismatchWhenExpandingDotsIn(JsonArray errorContainingJsonArray) {
175 1 1. typeMismatchWhenExpandingDotsIn : replaced return value with null for com/github/dakusui/symfonion/exceptions/ExceptionThrower::typeMismatchWhenExpandingDotsIn → NO_COVERAGE
    return new SymfonionIllegalFormatException(
176
        errorContainingJsonArray,
177
        "This array, only integers, nulls, and strings containing only dots (...) are allowed.",
178
        contextValueOf(JSON_ELEMENT_ROOT),
179
        contextValueOf(SOURCE_FILE) );
180
  }
181
182
  public static SymfonionMissingElementException requiredElementMissingException(JsonElement problemCausingJsonNode, Object relativePathFromProblemCausingJsonNode) throws SymfonionMissingElementException {
183
    throw new SymfonionMissingElementException(problemCausingJsonNode, relativePathFromProblemCausingJsonNode, contextValueOf(JSON_ELEMENT_ROOT), contextValueOf(SOURCE_FILE));
184
  }
185
186
  public static SymfonionMissingElementException requiredElementMissingException(JsonElement actualJSON, JsonObject root, Object relPath) throws SymfonionMissingElementException {
187
    throw new SymfonionMissingElementException(actualJSON, relPath, root, contextValueOf(SOURCE_FILE));
188
  }
189
190
  public static SymfonionException deviceException(String msg, Throwable e) throws SymfonionException {
191
    throw new SymfonionException(msg, e, contextValueOf(SOURCE_FILE));
192
  }
193
194
  public static RuntimeException runtimeException(String msg, Throwable e) {
195
    throw new RuntimeException(msg, e);
196
  }
197
198
  public static FractionFormatException throwFractionFormatException(String fraction) throws FractionFormatException {
199
    throw new FractionFormatException(fraction);
200
  }
201
202
  public static SymfonionInterruptedException interrupted(InterruptedException e) {
203 1 1. interrupted : removed call to java/lang/Thread::interrupt → NO_COVERAGE
    Thread.currentThread().interrupt();
204
    throw new SymfonionInterruptedException(e.getMessage(), e);
205
  }
206
207
  public static CliException failedToRetrieveTransmitterFromMidiIn(MidiUnavailableException e, MidiDevice.Info inMidiDeviceInfo) {
208
    throw new CliException(format("(-) Failed to get transmitter from MIDI-in device (%s)", inMidiDeviceInfo.getName()), e);
209
  }
210
211
  public static CliException failedToOpenMidiDevice(MidiUnavailableException ee) {
212
    throw new CliException(format("(-) Failed to open MIDI-%s device (%s)",
213
        ExceptionThrower.<MidiDevice.Info>contextValueOf(MIDI_DEVICE_INFO),
214
        ExceptionThrower.<String>contextValueOf(MIDI_DEVICE_INFO_IO).toLowerCase()), ee);
215
  }
216
217
  public static CliException failedToAccessMidiDevice(String deviceType, MidiUnavailableException e, MidiDevice.Info[] matchedInfos) {
218
    throw new CliException(composeErrMsg(format("Failed to access MIDI-%s device:'%s'.", deviceType, matchedInfos[0].getName()), "O"), e);
219
  }
220
221
  public static RuntimeException multipleMidiDevices(MidiDeviceRecord e1, MidiDeviceRecord e2, Predicate<MidiDeviceRecord> cond) {
222
    throw new CliException(format("Multiple midi devices (at least: '%s', '%s') are found for: '%s'", e1, e2, cond));
223
  }
224
225
  public static RuntimeException noSuchMidiDeviceWasFound(Predicate<MidiDeviceRecord> cond) {
226
    throw new CliException(format("No such MIDI device was found for: '%s'", cond));
227
  }
228
229
  public static RuntimeException failedToGetTransmitter() {
230
    throw new RuntimeException();
231
  }
232
233
  public static RuntimeException failedToSetSequence() {
234
    throw new RuntimeException();
235
  }
236
237
  private static <T> T contextValueOf(ContextKey contextKey) {
238 1 1. contextValueOf : replaced return value with null for com/github/dakusui/symfonion/exceptions/ExceptionThrower::contextValueOf → KILLED
    return currentContext().get(contextKey);
239
  }
240
241
  private static Context currentContext() {
242 1 1. currentContext : negated conditional → KILLED
    if (context.get() == null)
243 1 1. currentContext : removed call to java/lang/ThreadLocal::set → KILLED
      context.set(new Context());
244 1 1. currentContext : replaced return value with null for com/github/dakusui/symfonion/exceptions/ExceptionThrower::currentContext → KILLED
    return context.get();
245
  }
246
247
  private static Class<?> classOfValueFor(ContextKey key) {
248
    class GivenKeyIsNull {
249
    }
250 2 1. classOfValueFor : replaced return value with null for com/github/dakusui/symfonion/exceptions/ExceptionThrower::classOfValueFor → KILLED
2. classOfValueFor : negated conditional → KILLED
    return key != null ? key.type : GivenKeyIsNull.class;
251
  }
252
}

Mutations

57

1.1
Location : set
Killed by : none
replaced return value with null for com/github/dakusui/symfonion/exceptions/ExceptionThrower$Context::set → SURVIVED

61

1.1
Location : parent
Killed by : com.github.dakusui.symfonion.tests.InvalidDataErrorTest.illegalNoteLength_01(com.github.dakusui.symfonion.tests.InvalidDataErrorTest)
replaced return value with null for com/github/dakusui/symfonion/exceptions/ExceptionThrower$Context::parent → KILLED

72

1.1
Location : get
Killed by : com.github.dakusui.symfonion.tests.MalformedTest.givenMalformed_brokenObject(com.github.dakusui.symfonion.tests.MalformedTest)
negated conditional → KILLED

74

1.1
Location : get
Killed by : com.github.dakusui.symfonion.tests.MalformedTest.givenMalformed_brokenObject(com.github.dakusui.symfonion.tests.MalformedTest)
negated conditional → KILLED

78

1.1
Location : get
Killed by : com.github.dakusui.symfonion.tests.InvalidDataErrorTest.illegalNoteLength_01(com.github.dakusui.symfonion.tests.InvalidDataErrorTest)
negated conditional → KILLED

80

1.1
Location : get
Killed by : com.github.dakusui.symfonion.tests.InvalidDataErrorTest.illegalNoteLength_01(com.github.dakusui.symfonion.tests.InvalidDataErrorTest)
replaced return value with null for com/github/dakusui/symfonion/exceptions/ExceptionThrower$Context::get → KILLED

82

1.1
Location : get
Killed by : com.github.dakusui.symfonion.tests.MalformedTest.givenMalformed_brokenObject(com.github.dakusui.symfonion.tests.MalformedTest)
replaced return value with null for com/github/dakusui/symfonion/exceptions/ExceptionThrower$Context::get → KILLED

88

1.1
Location : close
Killed by : none
removed call to java/lang/ThreadLocal::set → SURVIVED

92

1.1
Location : createChild
Killed by : com.github.dakusui.symfonion.tests.MalformedTest.givenMalformed_emptyFile(com.github.dakusui.symfonion.tests.MalformedTest)
replaced return value with null for com/github/dakusui/symfonion/exceptions/ExceptionThrower$Context::createChild → KILLED

102

1.1
Location : contextEntry
Killed by : com.github.dakusui.symfonion.tests.MalformedTest.givenMalformed_emptyFile(com.github.dakusui.symfonion.tests.MalformedTest)
replaced return value with null for com/github/dakusui/symfonion/exceptions/ExceptionThrower::contextEntry → KILLED

107

1.1
Location : $
Killed by : com.github.dakusui.symfonion.tests.MalformedTest.givenMalformed_emptyFile(com.github.dakusui.symfonion.tests.MalformedTest)
replaced return value with null for com/github/dakusui/symfonion/exceptions/ExceptionThrower::$ → KILLED

112

1.1
Location : context
Killed by : com.github.dakusui.symfonion.tests.MalformedTest.givenMalformed_emptyFile(com.github.dakusui.symfonion.tests.MalformedTest)
removed call to java/lang/ThreadLocal::set → KILLED

115

1.1
Location : context
Killed by : none
replaced return value with null for com/github/dakusui/symfonion/exceptions/ExceptionThrower::context → SURVIVED

143

1.1
Location : syntaxErrorInNotePattern
Killed by : none
replaced return value with null for com/github/dakusui/symfonion/exceptions/ExceptionThrower::syntaxErrorInNotePattern → NO_COVERAGE

167

1.1
Location : syntaxErrorWhenExpandingDotsIn
Killed by : none
replaced return value with null for com/github/dakusui/symfonion/exceptions/ExceptionThrower::syntaxErrorWhenExpandingDotsIn → NO_COVERAGE

175

1.1
Location : typeMismatchWhenExpandingDotsIn
Killed by : none
replaced return value with null for com/github/dakusui/symfonion/exceptions/ExceptionThrower::typeMismatchWhenExpandingDotsIn → NO_COVERAGE

203

1.1
Location : interrupted
Killed by : none
removed call to java/lang/Thread::interrupt → NO_COVERAGE

238

1.1
Location : contextValueOf
Killed by : com.github.dakusui.symfonion.tests.MalformedTest.givenMalformed_brokenObject(com.github.dakusui.symfonion.tests.MalformedTest)
replaced return value with null for com/github/dakusui/symfonion/exceptions/ExceptionThrower::contextValueOf → KILLED

242

1.1
Location : currentContext
Killed by : com.github.dakusui.symfonion.tests.MalformedTest.givenMalformed_emptyFile(com.github.dakusui.symfonion.tests.MalformedTest)
negated conditional → KILLED

243

1.1
Location : currentContext
Killed by : com.github.dakusui.symfonion.tests.InvalidDataErrorTest.illegalFraction(com.github.dakusui.symfonion.tests.InvalidDataErrorTest)
removed call to java/lang/ThreadLocal::set → KILLED

244

1.1
Location : currentContext
Killed by : com.github.dakusui.symfonion.tests.MalformedTest.givenMalformed_emptyFile(com.github.dakusui.symfonion.tests.MalformedTest)
replaced return value with null for com/github/dakusui/symfonion/exceptions/ExceptionThrower::currentContext → KILLED

250

1.1
Location : classOfValueFor
Killed by : com.github.dakusui.symfonion.tests.MalformedTest.givenMalformed_emptyFile(com.github.dakusui.symfonion.tests.MalformedTest)
replaced return value with null for com/github/dakusui/symfonion/exceptions/ExceptionThrower::classOfValueFor → KILLED

2.2
Location : classOfValueFor
Killed by : com.github.dakusui.symfonion.tests.MalformedTest.givenMalformed_emptyFile(com.github.dakusui.symfonion.tests.MalformedTest)
negated conditional → KILLED

Active mutators

Tests examined


Report generated by PIT 1.15.3