Song.java

1
package com.github.dakusui.symfonion.song;
2
3
import com.github.dakusui.symfonion.compat.exceptions.ExceptionContext;
4
import com.github.dakusui.symfonion.compat.exceptions.SymfonionException;
5
import com.github.dakusui.symfonion.compat.json.CompatJsonException;
6
import com.github.dakusui.symfonion.compat.json.JsonUtils;
7
import com.github.valid8j.pcond.forms.Predicates;
8
import com.google.gson.JsonArray;
9
import com.google.gson.JsonObject;
10
11
import java.util.LinkedList;
12
import java.util.List;
13
import java.util.Map;
14
import java.util.Set;
15
import java.util.function.Predicate;
16
17
import static com.github.dakusui.symfonion.compat.exceptions.CompatExceptionThrower.ContextKey.JSON_ELEMENT_ROOT;
18
import static com.github.dakusui.symfonion.compat.exceptions.CompatExceptionThrower.exceptionContext;
19
import static com.github.dakusui.symfonion.compat.exceptions.ExceptionContext.entry;
20
import static com.github.dakusui.symfonion.compat.json.JsonUtils.path;
21
import static com.github.dakusui.symfonion.song.CompatSong.Builder.initNoteMaps;
22
import static com.github.dakusui.symfonion.song.CompatSong.Builder.initParts;
23
import static com.github.valid8j.classic.Requires.requireNonNull;
24
import static com.github.valid8j.fluent.Expectations.precondition;
25
import static com.github.valid8j.fluent.Expectations.value;
26
import static java.util.Collections.unmodifiableList;
27
import static java.util.Collections.unmodifiableSet;
28
29
/**
30
 * //@formatter:off
31
 * [source, JSON]
32
 * .The **Song** file format
33
 * ----
34
 * {
35
 *   "parts": { "<partName1>": "<object:PartDefinition1>"
36
 *             },
37
 *   "$noteMaps": {
38
 *                  "<noteMapName1>": "<object:NoteMap>",
39
 *                  "<noteMapName2>": "<object:NoteMap>"
40
 *                },
41
 *   "sequence": [
42
 *                  "<object:Measure1>",
43
 *                  "<object:Measure2>",
44
 *                  "...",
45
 *                  "<object:MeasureN>"
46
 *                ]
47
 * }
48
 * ----
49
 *
50
 * The each "<Measure>" should be a JSON object and look like as follows:
51
 * ----
52
 * {
53
*    "beats": "16/16",
54
 *   "parts": {
55
 *     "piano": {
56
 *     },
57
 *     "guitar": {
58
 *     }
59
 *   },
60
 *   "groove": [ {}, {}, "...", {}],
61
 *   "labels": [ "label1" ]
62
 * }
63
 * ----
64
 *
65
 * //@formatter:on
66
 *
67
 * @see Measure
68
 */
69
public class Song {
70
  private final Map<String, Part> parts;
71
  private final List<Measure>     measures;
72
  private final JsonObject        rootJsonObject;
73
74
  Song(Map<String, Part> parts,
75
       List<Measure> bars,
76
       JsonObject rootJsonObject) {
77
    this.parts          = requireNonNull(parts);
78
    this.measures       = requireNonNull(bars);
79
    this.rootJsonObject = requireNonNull(rootJsonObject);
80
  }
81
82
  /**
83
   * Returns bars.
84
   *
85
   * @return Bars.
86
   */
87
  public List<Measure> measures() {
88 1 1. measures : replaced return value with Collections.emptyList for com/github/dakusui/symfonion/song/Song::measures → NO_COVERAGE
    return unmodifiableList(this.measures);
89
  }
90
91
  /**
92
   * Returns all known part names.
93
   *
94
   * @return A list of part names.
95
   */
96
  public Set<String> partNames() {
97 1 1. partNames : replaced return value with Collections.emptySet for com/github/dakusui/symfonion/song/Song::partNames → NO_COVERAGE
    return unmodifiableSet(this.parts.keySet());
98
  }
99
100
  public Part part(String name) {
101
    assert precondition(value(parts).invoke("containsKey", name)
102
                                    .asBoolean()
103
                                    .toBe()
104
                                    .trueValue());
105 1 1. part : replaced return value with null for com/github/dakusui/symfonion/song/Song::part → NO_COVERAGE
    return this.parts.get(name);
106
  }
107
108
  /**
109
   * Returns a root JSON object to which this bar belongs.
110
   *
111
   * @return A root JSON object.
112
   */
113
  public JsonObject rootJsonObject() {
114 1 1. rootJsonObject : replaced return value with null for com/github/dakusui/symfonion/song/Song::rootJsonObject → NO_COVERAGE
    return this.rootJsonObject;
115
  }
116
117
  public static class Builder {
118
    private final JsonObject json;
119
120
    private Predicate<Measure> measureFilter = Predicates.alwaysTrue();
121
    private Predicate<String>  partFilter    = Predicates.alwaysTrue();
122
123
    public Builder(JsonObject jsonObject) {
124
      this.json = requireNonNull(jsonObject);
125
    }
126
127
    public Builder measureFilter(Predicate<Measure> barFilter) {
128
      this.measureFilter = requireNonNull(barFilter);
129 1 1. measureFilter : replaced return value with null for com/github/dakusui/symfonion/song/Song$Builder::measureFilter → NO_COVERAGE
      return this;
130
    }
131
132
    public Builder partFilter(Predicate<String> partFilter) {
133
      this.partFilter = requireNonNull(partFilter);
134 1 1. partFilter : replaced return value with null for com/github/dakusui/symfonion/song/Song$Builder::partFilter → NO_COVERAGE
      return this;
135
    }
136
137
    public Song build() throws CompatJsonException, SymfonionException {
138
      try (ExceptionContext ignored = exceptionContext(entry(JSON_ELEMENT_ROOT, json))) {
139
        Map<String, NoteMap> noteMaps = initNoteMaps(json);
140 1 1. build : replaced return value with null for com/github/dakusui/symfonion/song/Song$Builder::build → NO_COVERAGE
        return new Song(initParts(this.json),
141
                        initMeasures(json,
142
                                     noteMaps,
143
                                     this.measureFilter,
144
                                     this.partFilter),
145
                        json);
146
      }
147
    }
148
149
    private List<Measure> initMeasures(JsonObject json,
150
                                       Map<String, NoteMap> noteMaps,
151
                                       Predicate<Measure> measureFilter,
152
                                       Predicate<String> partFilter) {
153
      List<Measure> ret      = new LinkedList<>();
154
      var           sequence = JsonUtils.findJsonArray(json, path(Keyword.sequence)).orElse(new JsonArray());
155
      for (var entry : sequence) {
156
        var measure = new Measure(entry.getAsJsonObject(), noteMaps, partFilter);
157 1 1. initMeasures : negated conditional → NO_COVERAGE
        if (measureFilter.test(measure))
158
          ret.add(measure);
159
      }
160 1 1. initMeasures : replaced return value with Collections.emptyList for com/github/dakusui/symfonion/song/Song$Builder::initMeasures → NO_COVERAGE
      return unmodifiableList(ret);
161
    }
162
  }
163
}

Mutations

88

1.1
Location : measures
Killed by : none
replaced return value with Collections.emptyList for com/github/dakusui/symfonion/song/Song::measures → NO_COVERAGE

97

1.1
Location : partNames
Killed by : none
replaced return value with Collections.emptySet for com/github/dakusui/symfonion/song/Song::partNames → NO_COVERAGE

105

1.1
Location : part
Killed by : none
replaced return value with null for com/github/dakusui/symfonion/song/Song::part → NO_COVERAGE

114

1.1
Location : rootJsonObject
Killed by : none
replaced return value with null for com/github/dakusui/symfonion/song/Song::rootJsonObject → NO_COVERAGE

129

1.1
Location : measureFilter
Killed by : none
replaced return value with null for com/github/dakusui/symfonion/song/Song$Builder::measureFilter → NO_COVERAGE

134

1.1
Location : partFilter
Killed by : none
replaced return value with null for com/github/dakusui/symfonion/song/Song$Builder::partFilter → NO_COVERAGE

140

1.1
Location : build
Killed by : none
replaced return value with null for com/github/dakusui/symfonion/song/Song$Builder::build → NO_COVERAGE

157

1.1
Location : initMeasures
Killed by : none
negated conditional → NO_COVERAGE

160

1.1
Location : initMeasures
Killed by : none
replaced return value with Collections.emptyList for com/github/dakusui/symfonion/song/Song$Builder::initMeasures → NO_COVERAGE

Active mutators

Tests examined


Report generated by PIT 1.19.1