| 1 | package com.github.dakusui.symfonion.song; | |
| 2 | ||
| 3 | import com.github.dakusui.logias.Logias; | |
| 4 | import com.github.dakusui.logias.lisp.Context; | |
| 5 | import com.github.dakusui.symfonion.compat.exceptions.ExceptionContext; | |
| 6 | import com.github.dakusui.symfonion.compat.exceptions.SymfonionException; | |
| 7 | import com.github.dakusui.symfonion.compat.json.CompatJsonException; | |
| 8 | import com.github.dakusui.symfonion.compat.json.CompatJsonUtils; | |
| 9 | import com.github.dakusui.symfonion.utils.Utils; | |
| 10 | import com.github.valid8j.classic.Requires; | |
| 11 | import com.github.valid8j.pcond.forms.Predicates; | |
| 12 | import com.google.gson.JsonArray; | |
| 13 | import com.google.gson.JsonElement; | |
| 14 | import com.google.gson.JsonObject; | |
| 15 | ||
| 16 | import java.util.*; | |
| 17 | import java.util.function.Predicate; | |
| 18 | ||
| 19 | import static com.github.dakusui.symfonion.compat.exceptions.CompatExceptionThrower.ContextKey.JSON_ELEMENT_ROOT; | |
| 20 | import static com.github.dakusui.symfonion.compat.exceptions.CompatExceptionThrower.exceptionContext; | |
| 21 | import static com.github.dakusui.symfonion.compat.exceptions.CompatExceptionThrower.typeMismatchException; | |
| 22 | import static com.github.dakusui.symfonion.compat.exceptions.ExceptionContext.entry; | |
| 23 | import static com.github.dakusui.symfonion.compat.exceptions.SymfonionTypeMismatchException.ARRAY; | |
| 24 | import static com.github.dakusui.symfonion.compat.exceptions.SymfonionTypeMismatchException.OBJECT; | |
| 25 | import static com.github.valid8j.classic.Requires.requireNonNull; | |
| 26 | import static java.util.Collections.unmodifiableList; | |
| 27 | import static java.util.Collections.unmodifiableSet; | |
| 28 | ||
| 29 | /** | |
| 30 | * //@formatter:off | |
| 31 | * This class models the logical aspect of the entire musical piece, described by the application syntax. | |
| 32 | * | |
| 33 | * Entries that directly matter in this class are as follows: | |
| 34 | * | |
| 35 | * .Entries `(Compat)Song` class processes | |
| 36 | * ---- | |
| 37 | * { | |
| 38 | * "$noteMaps": { "<noteMapName>": "<NoteMap>"}, | |
| 39 | * "parts": { "<partName>": "<Part>" }, | |
| 40 | * "grooves": { "<grooveName>": ["<Groove>", "<Groove>", "..."] }, | |
| 41 | * "sequence": [ "<Bar>", "<Bar>", "..." ] | |
| 42 | * } | |
| 43 | * ---- | |
| 44 | * | |
| 45 | * Each element in `$sequence` array is a {@link Bar} object. | |
| 46 | * A bar contains notes and its related information played during a certain period of time. | |
| 47 | * | |
| 48 | * //@formatter:on | |
| 49 | */ | |
| 50 | public class CompatSong { | |
| 51 | private final Map<String, Part> parts; | |
| 52 | private final Map<String, Groove> grooves; | |
| 53 | private final List<Bar> bars; | |
| 54 | private final JsonObject rootJsonObject; | |
| 55 | ||
| 56 | /** | |
| 57 | * Creates an object of this class. | |
| 58 | * | |
| 59 | * @param parts Parts of a musical work. | |
| 60 | * @param grooves Grooves referenced from `bars`. | |
| 61 | * @param bars Bars of a musical work. | |
| 62 | * @param rootJsonObject A root JSON object that `barJsonObject` belongs to. | |
| 63 | */ | |
| 64 | public CompatSong(Map<String, Part> parts, | |
| 65 | Map<String, Groove> grooves, | |
| 66 | List<Bar> bars, | |
| 67 | JsonObject rootJsonObject) { | |
| 68 | this.parts = Requires.requireNonNull(parts); | |
| 69 | this.grooves = requireNonNull(grooves); | |
| 70 | this.bars = requireNonNull(bars); | |
| 71 | this.rootJsonObject = requireNonNull(rootJsonObject); | |
| 72 | } | |
| 73 | ||
| 74 | /** | |
| 75 | * Returns bars. | |
| 76 | * | |
| 77 | * @return Bars. | |
| 78 | */ | |
| 79 | public List<Bar> bars() { | |
| 80 |
1
1. bars : replaced return value with Collections.emptyList for com/github/dakusui/symfonion/song/CompatSong::bars → KILLED |
return unmodifiableList(this.bars); |
| 81 | } | |
| 82 | ||
| 83 | /** | |
| 84 | * Returns all known part names. | |
| 85 | * | |
| 86 | * @return A list of part names. | |
| 87 | */ | |
| 88 | public Set<String> partNames() { | |
| 89 |
1
1. partNames : replaced return value with Collections.emptySet for com/github/dakusui/symfonion/song/CompatSong::partNames → KILLED |
return unmodifiableSet(this.parts.keySet()); |
| 90 | } | |
| 91 | ||
| 92 | public Part part(String name) { | |
| 93 |
1
1. part : replaced return value with null for com/github/dakusui/symfonion/song/CompatSong::part → KILLED |
return this.parts.get(name); |
| 94 | } | |
| 95 | ||
| 96 | public Groove groove(String grooveName) { | |
| 97 |
1
1. groove : replaced return value with null for com/github/dakusui/symfonion/song/CompatSong::groove → NO_COVERAGE |
return this.grooves.get(grooveName); |
| 98 | } | |
| 99 | ||
| 100 | /** | |
| 101 | * Returns a root JSON object to which this bar belongs. | |
| 102 | * | |
| 103 | * @return A root JSON object. | |
| 104 | */ | |
| 105 | public JsonObject rootJsonObject() { | |
| 106 |
1
1. rootJsonObject : replaced return value with null for com/github/dakusui/symfonion/song/CompatSong::rootJsonObject → KILLED |
return this.rootJsonObject; |
| 107 | } | |
| 108 | ||
| 109 | public static class Builder { | |
| 110 | private final JsonObject json; | |
| 111 | ||
| 112 | private Predicate<Bar> barFilter = Predicates.alwaysTrue(); | |
| 113 | private Predicate<String> partFilter = Predicates.alwaysTrue(); | |
| 114 | ||
| 115 | public Builder(JsonObject jsonObject) { | |
| 116 | this.json = requireNonNull(jsonObject); | |
| 117 | } | |
| 118 | ||
| 119 | public Builder barFilter(Predicate<Bar> barFilter) { | |
| 120 | this.barFilter = requireNonNull(barFilter); | |
| 121 |
1
1. barFilter : replaced return value with null for com/github/dakusui/symfonion/song/CompatSong$Builder::barFilter → KILLED |
return this; |
| 122 | } | |
| 123 | ||
| 124 | public Builder partFilter(Predicate<String> partFilter) { | |
| 125 | this.partFilter = requireNonNull(partFilter); | |
| 126 |
1
1. partFilter : replaced return value with null for com/github/dakusui/symfonion/song/CompatSong$Builder::partFilter → KILLED |
return this; |
| 127 | } | |
| 128 | ||
| 129 | public CompatSong build() throws CompatJsonException, SymfonionException { | |
| 130 | try (ExceptionContext ignored = exceptionContext(entry(JSON_ELEMENT_ROOT, json))) { | |
| 131 | Map<String, NoteMap> noteMaps = initNoteMaps(json); | |
| 132 | Map<String, Groove> grooves = initGrooves(json); | |
| 133 |
1
1. build : replaced return value with null for com/github/dakusui/symfonion/song/CompatSong$Builder::build → KILLED |
return new CompatSong(initParts(this.json), |
| 134 | grooves, | |
| 135 | initBars(json, | |
| 136 | grooves, | |
| 137 | noteMaps, | |
| 138 | this.barFilter, | |
| 139 | this.partFilter), | |
| 140 | json); | |
| 141 | } | |
| 142 | } | |
| 143 | ||
| 144 | public static Context loadMidiDeviceProfile(JsonObject json, Context logiasContext) throws SymfonionException, CompatJsonException { | |
| 145 | JsonElement tmp = CompatJsonUtils.asJsonObjectWithDefault(json, new JsonObject(), Keyword.settings); | |
| 146 |
1
1. loadMidiDeviceProfile : negated conditional → KILLED |
if (!tmp.isJsonObject()) { |
| 147 | throw typeMismatchException(tmp, OBJECT); | |
| 148 | } | |
| 149 | String profileName = CompatJsonUtils.asStringWithDefault(tmp.getAsJsonObject(), "", Keyword.mididevice); | |
| 150 | Logias logias = new Logias(logiasContext); | |
| 151 |
1
1. loadMidiDeviceProfile : negated conditional → KILLED |
if (!"".equals(profileName)) { |
| 152 | JsonObject deviceDef = CompatJsonUtils.toJson(Utils.loadResource(profileName + ".json")).getAsJsonObject(); | |
| 153 | Iterator<String> i = CompatJsonUtils.keyIterator(deviceDef); | |
| 154 |
1
1. loadMidiDeviceProfile : negated conditional → NO_COVERAGE |
while (i.hasNext()) { |
| 155 | String k = i.next(); | |
| 156 | JsonElement v = deviceDef.get(k); | |
| 157 | logiasContext.bind(k, logias.run(logias.buildSexp(v))); | |
| 158 | } | |
| 159 | } | |
| 160 |
1
1. loadMidiDeviceProfile : replaced return value with null for com/github/dakusui/symfonion/song/CompatSong$Builder::loadMidiDeviceProfile → SURVIVED |
return logiasContext; |
| 161 | } | |
| 162 | ||
| 163 | private static List<Bar> initBars( | |
| 164 | JsonObject json, | |
| 165 | Map<String, Groove> grooves, | |
| 166 | Map<String, NoteMap> noteMaps, | |
| 167 | Predicate<Bar> barFilter, | |
| 168 | Predicate<String> partFilter) throws SymfonionException, CompatJsonException { | |
| 169 | List<Bar> bars = new LinkedList<>(); | |
| 170 | try (ExceptionContext ignored = exceptionContext(entry(JSON_ELEMENT_ROOT, json))) { | |
| 171 | JsonElement tmp = CompatJsonUtils.asJsonElement(json, Keyword.sequence); | |
| 172 |
1
1. initBars : negated conditional → KILLED |
if (!tmp.isJsonArray()) { |
| 173 | throw typeMismatchException(tmp, ARRAY); | |
| 174 | } | |
| 175 | JsonArray seqJson = tmp.getAsJsonArray(); | |
| 176 | int len = seqJson.getAsJsonArray().size(); | |
| 177 |
2
1. initBars : changed conditional boundary → KILLED 2. initBars : negated conditional → KILLED |
for (int i = 0; i < len; i++) { |
| 178 | JsonElement barJson = seqJson.get(i); | |
| 179 |
1
1. initBars : negated conditional → KILLED |
if (!barJson.isJsonObject()) { |
| 180 | throw typeMismatchException(seqJson, OBJECT); | |
| 181 | } | |
| 182 | Bar bar = new Bar(barJson.getAsJsonObject(), | |
| 183 | grooves, | |
| 184 | noteMaps, | |
| 185 | partFilter); | |
| 186 |
1
1. initBars : negated conditional → KILLED |
if (barFilter.test(bar)) |
| 187 | bars.add(bar); | |
| 188 | } | |
| 189 | } | |
| 190 |
1
1. initBars : replaced return value with Collections.emptyList for com/github/dakusui/symfonion/song/CompatSong$Builder::initBars → KILLED |
return bars; |
| 191 | } | |
| 192 | ||
| 193 | /** | |
| 194 | * Returns a map of note-map name to note-map. | |
| 195 | * | |
| 196 | * @param json A JSON object that defines note-maps. | |
| 197 | * @return A map of note-map name to note-map. | |
| 198 | * @throws SymfonionException An error found in {@code json} argument. | |
| 199 | * @throws CompatJsonException An error found in {@code json} argument. | |
| 200 | */ | |
| 201 | static Map<String, NoteMap> initNoteMaps(JsonObject json) throws SymfonionException, CompatJsonException { | |
| 202 | Map<String, NoteMap> noteMaps = new HashMap<>(); | |
| 203 | final JsonObject noteMapsJSON = CompatJsonUtils.asJsonObjectWithDefault(json, new JsonObject(), Keyword.notemaps); | |
| 204 | ||
| 205 | Iterator<String> i = CompatJsonUtils.keyIterator(noteMapsJSON); | |
| 206 | noteMaps.put(Keyword.normal.toString(), NoteMap.defaultNoteMap); | |
| 207 | noteMaps.put(Keyword.percussion.toString(), NoteMap.defaultPercussionMap); | |
| 208 |
1
1. initNoteMaps : negated conditional → KILLED |
while (i.hasNext()) { |
| 209 | String noteMapName = i.next(); | |
| 210 | NoteMap cur = new NoteMap(CompatJsonUtils.asJsonObject(noteMapsJSON, noteMapName)); | |
| 211 | noteMaps.put(noteMapName, cur); | |
| 212 | } | |
| 213 |
1
1. initNoteMaps : replaced return value with Collections.emptyMap for com/github/dakusui/symfonion/song/CompatSong$Builder::initNoteMaps → KILLED |
return noteMaps; |
| 214 | } | |
| 215 | ||
| 216 | static Map<String, Part> initParts(JsonObject json) throws SymfonionException, CompatJsonException { | |
| 217 | Map<String, Part> parts = new HashMap<>(); | |
| 218 |
1
1. initParts : negated conditional → KILLED |
if (CompatJsonUtils.hasPath(json, Keyword.parts)) { |
| 219 | JsonObject instrumentsJSON = CompatJsonUtils.asJsonObject(json, Keyword.parts); | |
| 220 | Iterator<String> i = CompatJsonUtils.keyIterator(instrumentsJSON); | |
| 221 |
1
1. initParts : negated conditional → KILLED |
while (i.hasNext()) { |
| 222 | String name = i.next(); | |
| 223 | Part cur = new Part(name, CompatJsonUtils.asJsonObject(instrumentsJSON, name)); | |
| 224 | parts.put(name, cur); | |
| 225 | } | |
| 226 | } | |
| 227 |
1
1. initParts : replaced return value with Collections.emptyMap for com/github/dakusui/symfonion/song/CompatSong$Builder::initParts → KILLED |
return parts; |
| 228 | } | |
| 229 | ||
| 230 | static Map<String, Groove> initGrooves(JsonObject json) throws SymfonionException, CompatJsonException { | |
| 231 | Map<String, Groove> grooves = new HashMap<>(); | |
| 232 |
1
1. initGrooves : negated conditional → KILLED |
if (CompatJsonUtils.hasPath(json, Keyword.grooves)) { |
| 233 | JsonObject groovesJSON = CompatJsonUtils.asJsonObject(json, Keyword.grooves); | |
| 234 | ||
| 235 | Iterator<String> i = CompatJsonUtils.keyIterator(groovesJSON); | |
| 236 |
1
1. initGrooves : negated conditional → KILLED |
while (i.hasNext()) { |
| 237 | String name = i.next(); | |
| 238 | Groove cur = Groove.createGroove(CompatJsonUtils.asJsonArray(groovesJSON, name)); | |
| 239 | grooves.put(name, cur); | |
| 240 | } | |
| 241 | } | |
| 242 |
1
1. initGrooves : replaced return value with Collections.emptyMap for com/github/dakusui/symfonion/song/CompatSong$Builder::initGrooves → KILLED |
return grooves; |
| 243 | } | |
| 244 | } | |
| 245 | } | |
Mutations | ||
| 80 |
1.1 |
|
| 89 |
1.1 |
|
| 93 |
1.1 |
|
| 97 |
1.1 |
|
| 106 |
1.1 |
|
| 121 |
1.1 |
|
| 126 |
1.1 |
|
| 133 |
1.1 |
|
| 146 |
1.1 |
|
| 151 |
1.1 |
|
| 154 |
1.1 |
|
| 160 |
1.1 |
|
| 172 |
1.1 |
|
| 177 |
1.1 2.2 |
|
| 179 |
1.1 |
|
| 186 |
1.1 |
|
| 190 |
1.1 |
|
| 208 |
1.1 |
|
| 213 |
1.1 |
|
| 218 |
1.1 |
|
| 221 |
1.1 |
|
| 227 |
1.1 |
|
| 232 |
1.1 |
|
| 236 |
1.1 |
|
| 242 |
1.1 |