Skip to content

Commit

Permalink
feat!: [Java] 列挙型を定数の集合にしてnon exhaustiveに (#955)
Browse files Browse the repository at this point in the history
#941 の続き。また列挙型の網羅性についての議論は以下。
#950 (comment)
  • Loading branch information
qryxip authored Jan 29, 2025
1 parent 23813b1 commit 2adee67
Show file tree
Hide file tree
Showing 8 changed files with 159 additions and 48 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
package jp.hiroshiba.voicevoxcore;

/** ハードウェアアクセラレーションモード。 */
public enum AccelerationMode {
public class AccelerationMode {
/** 実行環境に合わせて自動的に選択する。 */
AUTO,
public static final AccelerationMode AUTO = new AccelerationMode("AUTO");

/** CPUに設定する。 */
CPU,
public static final AccelerationMode CPU = new AccelerationMode("CPU");

/** GPUに設定する。 */
GPU,
public static final AccelerationMode GPU = new AccelerationMode("GPU");

private final String identifier;

private AccelerationMode(String identifier) {
this.identifier = identifier;
}

@Override
public String toString() {
return identifier;
}
}
Original file line number Diff line number Diff line change
@@ -1,27 +1,67 @@
package jp.hiroshiba.voicevoxcore;

import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import java.lang.reflect.Type;

/** スタイル(style)に対応するモデルの種類。 */
public enum StyleType {
public class StyleType {
/** 音声合成クエリの作成と音声合成が可能。 */
@SerializedName("talk")
@Expose
TALK,
public static final StyleType TALK = new StyleType("talk");

/** 歌唱音声合成用のクエリの作成が可能。 */
@SerializedName("singing_teacher")
@Expose
SINGING_TEACHER,
public static final StyleType SINGING_TEACHER = new StyleType("singing_teacher");

/** 歌唱音声合成が可能。 */
@SerializedName("frame_decode")
@Expose
FRAME_DECODE,
public static final StyleType FRAME_DECODE = new StyleType("frame_decode");

/** 歌唱音声合成用のクエリの作成と歌唱音声合成が可能。 */
@SerializedName("sing")
@Expose
SING,
public static final StyleType SING = new StyleType("sing");

public static final class Serializer implements JsonSerializer<StyleType> {
@Override
public JsonElement serialize(StyleType src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(src.toString());
}
}

public static final class Deserializer implements JsonDeserializer<StyleType> {
@Override
public StyleType deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
String value = json.getAsString();
switch (value) {
case "talk":
return TALK;
case "singing_teacher":
return SINGING_TEACHER;
case "frame_decode":
return FRAME_DECODE;
case "sing":
return SING;
default:
throw new JsonParseException(
String.format(
"Invalid variant: `%s`, expected one of "
+ "`talk`, `singing_teacher`, `frame_decode`, `sing`",
value));
}
}
}

private final String identifier;

private StyleType(String identifier) {
this.identifier = identifier;
}

@Override
public String toString() {
return identifier;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
package jp.hiroshiba.voicevoxcore;

import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
import jakarta.annotation.Nonnull;
Expand Down Expand Up @@ -114,30 +121,66 @@ public UserDictWord priority(int priority) {
private static native void rsValidatePronunciation(String pronunciation);

/** 単語の種類。 */
public static enum Type {
public static class Type {
/** 固有名詞。 */
@SerializedName("PROPER_NOUN")
@Expose
PROPER_NOUN,
public static final Type PROPER_NOUN = new Type("PROPER_NOUN");

/** 一般名詞。 */
@SerializedName("COMMON_NOUN")
@Expose
COMMON_NOUN,
public static final Type COMMON_NOUN = new Type("COMMON_NOUN");

/** 動詞。 */
@SerializedName("VERB")
@Expose
VERB,
public static final Type VERB = new Type("VERB");

/** 形容詞。 */
@SerializedName("ADJECTIVE")
@Expose
ADJECTIVE,
public static final Type ADJECTIVE = new Type("ADJECTIVE");

/** 語尾。 */
@SerializedName("SUFFIX")
@Expose
SUFFIX,
public static final Type SUFFIX = new Type("SUFFIX");

public static final class Serializer implements JsonSerializer<Type> {
@Override
public JsonElement serialize(
Type src, java.lang.reflect.Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(src.toString());
}
}

public static final class Deserializer implements JsonDeserializer<Type> {
@Override
public Type deserialize(
JsonElement json, java.lang.reflect.Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
String value = json.getAsString();
switch (value) {
case "PROPER_NOUN":
return PROPER_NOUN;
case "COMMON_NOUN":
return COMMON_NOUN;
case "VERB":
return VERB;
case "ADJECTIVE":
return ADJECTIVE;
case "SUFFIX":
return SUFFIX;
default:
throw new JsonParseException(
String.format(
"Invalid variant `%s`, expected one of "
+ "`PROPER_NOUN`, `COMMON_NOUN`, `VERB`, `ADJECTIVE`, `SUFFIX`",
value));
}
}
}

private final String identifier;

private Type(String identifier) {
this.identifier = identifier;
}

@Override
public String toString() {
return identifier;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package jp.hiroshiba.voicevoxcore.blocking;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import jakarta.annotation.Nonnull;
import java.util.ArrayList;
import java.util.Arrays;
Expand All @@ -11,6 +12,7 @@
import jp.hiroshiba.voicevoxcore.AccentPhrase;
import jp.hiroshiba.voicevoxcore.AudioQuery;
import jp.hiroshiba.voicevoxcore.CharacterMeta;
import jp.hiroshiba.voicevoxcore.StyleType;
import jp.hiroshiba.voicevoxcore.exceptions.InvalidModelDataException;
import jp.hiroshiba.voicevoxcore.exceptions.RunModelException;
import jp.hiroshiba.voicevoxcore.internal.Dll;
Expand Down Expand Up @@ -64,7 +66,9 @@ public boolean isGpuMode() {
*/
@Nonnull
public CharacterMeta[] metas() {
Gson gson = new Gson();
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(StyleType.class, new StyleType.Deserializer());
Gson gson = gsonBuilder.create();
String metasJson = rsGetMetasJson();
CharacterMeta[] rawMetas = gson.fromJson(metasJson, CharacterMeta[].class);
if (rawMetas == null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package jp.hiroshiba.voicevoxcore.blocking;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.internal.LinkedTreeMap;
import jakarta.annotation.Nonnull;
import java.io.File;
Expand Down Expand Up @@ -37,7 +38,9 @@ protected void finalize() throws Throwable {
*/
@Nonnull
public String addWord(UserDictWord word) {
Gson gson = new Gson();
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(UserDictWord.Type.class, new UserDictWord.Type.Serializer());
Gson gson = gsonBuilder.create();
String wordJson = gson.toJson(word);

return rsAddWord(wordJson);
Expand All @@ -50,7 +53,9 @@ public String addWord(UserDictWord word) {
* @param word 新しい単語のデータ。
*/
public void updateWord(String uuid, UserDictWord word) {
Gson gson = new Gson();
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(UserDictWord.Type.class, new UserDictWord.Type.Serializer());
Gson gson = gsonBuilder.create();
String wordJson = gson.toJson(word);

rsUpdateWord(uuid, wordJson);
Expand Down Expand Up @@ -142,7 +147,9 @@ public void save(String path) throws SaveUserDictException {
@Nonnull
public HashMap<String, UserDictWord> toHashMap() {
String json = rsGetWords();
Gson gson = new Gson();
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(UserDictWord.Type.class, new UserDictWord.Type.Deserializer());
Gson gson = gsonBuilder.create();
@SuppressWarnings("unchecked")
HashMap<String, LinkedTreeMap<String, ?>> rawWords = gson.fromJson(json, HashMap.class);
if (rawWords == null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package jp.hiroshiba.voicevoxcore.blocking;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import jakarta.annotation.Nonnull;
import java.io.Closeable;
import java.util.UUID;
import jp.hiroshiba.voicevoxcore.CharacterMeta;
import jp.hiroshiba.voicevoxcore.StyleType;
import jp.hiroshiba.voicevoxcore.internal.Dll;

/** 音声モデルファイル。 */
Expand Down Expand Up @@ -33,7 +35,9 @@ public VoiceModelFile(String modelPath) {
rsOpen(modelPath);
id = rsGetId();
String metasJson = rsGetMetasJson();
Gson gson = new Gson();
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(StyleType.class, new StyleType.Deserializer());
Gson gson = gsonBuilder.create();
CharacterMeta[] rawMetas = gson.fromJson(metasJson, CharacterMeta[].class);
if (rawMetas == null) {
throw new RuntimeException("Failed to parse metasJson");
Expand Down
8 changes: 4 additions & 4 deletions crates/voicevox_core_java_api/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ macro_rules! object_type {
};
}
#[macro_export]
macro_rules! enum_object {
($env: ident, $name: literal, $variant: literal) => {
$env.get_static_field(object!($name), $variant, object_type!($name))
macro_rules! static_field {
($env: ident, $name: literal, $field: literal) => {
$env.get_static_field(object!($name), $field, object_type!($name))
.unwrap_or_else(|_| {
panic!(
"Failed to get field {}",
concat!($variant, "L", object!($name), ";")
concat!($field, "L", object!($name), ";")
)
})
.l()
Expand Down
8 changes: 4 additions & 4 deletions crates/voicevox_core_java_api/src/synthesizer.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
common::{throw_if_err, JNIEnvExt as _, JavaApiError},
enum_object, object, object_type,
object, object_type, static_field,
};

use jni::{
Expand Down Expand Up @@ -31,9 +31,9 @@ unsafe extern "system" fn Java_jp_hiroshiba_voicevoxcore_blocking_Synthesizer_rs
let acceleration_mode = if acceleration_mode.is_null() {
Default::default()
} else {
let auto = enum_object!(env, "AccelerationMode", "AUTO")?;
let cpu = enum_object!(env, "AccelerationMode", "CPU")?;
let gpu = enum_object!(env, "AccelerationMode", "GPU")?;
let auto = static_field!(env, "AccelerationMode", "AUTO")?;
let cpu = static_field!(env, "AccelerationMode", "CPU")?;
let gpu = static_field!(env, "AccelerationMode", "GPU")?;
if env.is_same_object(&acceleration_mode, auto)? {
voicevox_core::AccelerationMode::Auto
} else if env.is_same_object(&acceleration_mode, cpu)? {
Expand Down

0 comments on commit 2adee67

Please sign in to comment.