| 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 |
|
| 61 |
1.1 |
|
| 72 |
1.1 |
|
| 74 |
1.1 |
|
| 78 |
1.1 |
|
| 80 |
1.1 |
|
| 82 |
1.1 |
|
| 88 |
1.1 |
|
| 92 |
1.1 |
|
| 102 |
1.1 |
|
| 107 |
1.1 |
|
| 112 |
1.1 |
|
| 115 |
1.1 |
|
| 143 |
1.1 |
|
| 167 |
1.1 |
|
| 175 |
1.1 |
|
| 203 |
1.1 |
|
| 238 |
1.1 |
|
| 242 |
1.1 |
|
| 243 |
1.1 |
|
| 244 |
1.1 |
|
| 250 |
1.1 2.2 |