Repository: apache/dubbo-go-hessian2 (go)

Issue:
Add Go struct refer to java.util.Locale
<!-- Please only use this template for submitting enhancement requests -->

**What would you like to be added**:

Add Go struct related to java.util.Locale

**Why is this needed**:

to support jdk classes.

Pull request:
Java.util.Locale support
<!--  Thanks for sending a pull request! 
-->

**What this PR does**:

**Which issue(s) this PR fixes**:
<!--
*Automatically closes linked issue when PR is merged.
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
_If PR is about `failing-tests or flakes`, please post the related issues/tests in a comment and do not use `Fixes`_*
-->
Fixes #230 

**Special notes for your reviewer**:add go struct Locale

**Does this PR introduce a user-facing change?**:
<!--
If no, just write "NONE" in the release-note block below.
If yes, a release note is required:
Enter your extended release note in the block below. If the PR requires additional action from users switching to the new release, include the string "action required".
-->
```release-note
add go struct Locale
```

Patch:
diff --git a/java_util.go b/java_util.go
index bbb4d99..8dacc1f 100644
--- a/java_util.go
+++ b/java_util.go
@@ -26,4 +26,7 @@ func init() {
 		MostSigBits:  int64(200),
 		LeastSigBits: int64(200),
 	})
+	RegisterPOJO(&java_util.LocaleHandle{
+		Value: "",
+	})
 }
diff --git a/java_util/README.md b/java_util/README.md
index 0b169cd..193f26b 100644
--- a/java_util/README.md
+++ b/java_util/README.md
@@ -5,3 +5,7 @@
 - dubbo-go-hessian2 cannot create UUID strut
 - see jdk source code of class:[java.util.UUID] learning how to create UUID struct
 - see https://github.com/satori/go.uuid
+2. explain dubbo-go-hession2 strut locale 
+-java object locale -> go struct Locale (PASS), but currently implemented are objects enumerated in java. See class:java.util.Locale
+-First convert to struct LocaleHandle and then call `GetLocaleFromHandler(localeHandler *LocaleHandle)` function to convert to struct Locale 
+-You can use the `language.ParseBase("zh-CN")` function in the `golang.org/x/text/language` package to convert the value of `locale.String()` get go struct and do other things  
\ No newline at end of file
diff --git a/java_util/README_CN.md b/java_util/README_CN.md
index bc1281f..a195dc1 100644
--- a/java_util/README_CN.md
+++ b/java_util/README_CN.md
@@ -5,3 +5,8 @@
 - java-server 提供的 uuid 可以解析，可以通过 UUID 的 ToString()函数解析成字符串，但是 go 目前未提供生成 uuid 的功能
 - java uuid 生成可以参考 jdk 下 java.util.UUID 类的相关源码
 - uuid 结构体创建参考 https://github.com/satori/go.uuid
+
+2. locale对象 
+- Locale.java中的对象可以转换成go的结构体Locale，但目前实现的是java中枚举的对象，具体见 class:java.util.Locale
+- 先转换成LocaleHandle对象，然后调用 `GetLocaleFromHandler(localeHandler *LocaleHandle)` 函数转成Locale对象
+- 可以使用 `golang.org/x/text/language` 包中的 `language.ParseBase("zh-CN")` 函数将 `locale.String()` 值转成go中的相关对象进行后续操作
\ No newline at end of file
diff --git a/java_util/locale.go b/java_util/locale.go
new file mode 100644
index 0000000..c785880
--- /dev/null
+++ b/java_util/locale.go
@@ -0,0 +1,202 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java_util
+
+// LocaleEnum is Locale enumeration value
+type LocaleEnum int
+
+// Locale struct enum
+const (
+	ENGLISH LocaleEnum = iota
+	FRENCH
+	GERMAN
+	ITALIAN
+	JAPANESE
+	KOREAN
+	CHINESE
+	SIMPLIFIED_CHINESE
+	TRADITIONAL_CHINESE
+	FRANCE
+	GERMANY
+	ITALY
+	JAPAN
+	KOREA
+	CHINA
+	PRC
+	TAIWAN
+	UK
+	US
+	CANADA
+	CANADA_FRENCH
+	ROOT
+)
+
+// Locale => java.util.Locale
+type Locale struct {
+	// ID is used to implement enumeration
+	id     LocaleEnum
+	lang   string
+	county string
+}
+
+func (locale *Locale) County() string {
+	return locale.county
+}
+
+func (locale *Locale) Lang() string {
+	return locale.lang
+}
+
+func (locale *Locale) String() string {
+	if len(locale.county) != 0 {
+		return locale.lang + "_" + locale.county
+	}
+	return locale.lang
+}
+
+// LocaleHandle => com.alibaba.com.caucho.hessian.io.LocaleHandle object
+type LocaleHandle struct {
+	Value string `hessian:"value"`
+}
+
+func (LocaleHandle) JavaClassName() string {
+	return "com.alibaba.com.caucho.hessian.io.LocaleHandle"
+}
+
+// locales is all const Locale struct slice
+// localeMap is key = locale.String() value = locale struct
+var (
+	locales   []Locale            = make([]Locale, 22, 22)
+	localeMap map[string](Locale) = make(map[string](Locale), 22)
+)
+
+// init java.util.Locale static object
+func init() {
+	locales[ENGLISH] = Locale{
+		id:     ENGLISH,
+		lang:   "en",
+		county: "",
+	}
+	locales[FRENCH] = Locale{
+		id:     FRENCH,
+		lang:   "fr",
+		county: "",
+	}
+	locales[GERMAN] = Locale{
+		id:     GERMAN,
+		lang:   "de",
+		county: "",
+	}
+	locales[ITALIAN] = Locale{
+		id:     ITALIAN,
+		lang:   "it",
+		county: "",
+	}
+	locales[JAPANESE] = Locale{
+		id:     JAPANESE,
+		lang:   "ja",
+		county: "",
+	}
+	locales[KOREAN] = Locale{
+		id:     KOREAN,
+		lang:   "ko",
+		county: "",
+	}
+	locales[CHINESE] = Locale{
+		id:     CHINESE,
+		lang:   "zh",
+		county: "",
+	}
+	locales[SIMPLIFIED_CHINESE] = Locale{
+		id:     SIMPLIFIED_CHINESE,
+		lang:   "zh",
+		county: "CN",
+	}
+	locales[TRADITIONAL_CHINESE] = Locale{
+		id:     TRADITIONAL_CHINESE,
+		lang:   "zh",
+		county: "TW",
+	}
+	locales[FRANCE] = Locale{
+		id:     FRANCE,
+		lang:   "fr",
+		county: "FR",
+	}
+	locales[GERMANY] = Locale{
+		id:     GERMANY,
+		lang:   "de",
+		county: "DE",
+	}
+	locales[ITALY] = Locale{
+		id:     ITALY,
+		lang:   "it",
+		county: "it",
+	}
+	locales[JAPAN] = Locale{
+		id:     JAPAN,
+		lang:   "ja",
+		county: "JP",
+	}
+	locales[KOREA] = Locale{
+		id:     KOREA,
+		lang:   "ko",
+		county: "KR",
+	}
+	locales[CHINA] = locales[SIMPLIFIED_CHINESE]
+	locales[PRC] = locales[SIMPLIFIED_CHINESE]
+	locales[TAIWAN] = locales[TRADITIONAL_CHINESE]
+	locales[UK] = Locale{
+		id:     UK,
+		lang:   "en",
+		county: "GB",
+	}
+	locales[US] = Locale{
+		id:     US,
+		lang:   "en",
+		county: "US",
+	}
+	locales[CANADA] = Locale{
+		id:     CANADA,
+		lang:   "en",
+		county: "CA",
+	}
+	locales[CANADA_FRENCH] = Locale{
+		id:     CANADA_FRENCH,
+		lang:   "fr",
+		county: "CA",
+	}
+	locales[ROOT] = Locale{
+		id:     ROOT,
+		lang:   "",
+		county: "",
+	}
+	for _, locale := range locales {
+		localeMap[locale.String()] = locale
+	}
+}
+
+// ToLocale get locale from enum
+func ToLocale(e LocaleEnum) *Locale {
+	return &locales[e]
+}
+
+// GetLocaleFromHandler is use LocaleHandle get Locale
+func GetLocaleFromHandler(localeHandler *LocaleHandle) *Locale {
+	locale := localeMap[localeHandler.Value]
+	return &locale
+}


Test patch:
diff --git a/java_util_test.go b/java_util_test.go
index ee6340e..6a9028c 100644
--- a/java_util_test.go
+++ b/java_util_test.go
@@ -53,3 +53,76 @@ func TestJavaUtil(t *testing.T) {
 	assert.Equal(t, uuid1.String(), resUuid1String)
 	assert.Equal(t, (resUuid2.(*java_util.UUID)).String(), resUuid2String)
 }
+
+func TestJavaUtilLocale(t *testing.T) {
+	res, err := decodeJavaResponse(`customReplyLocale`, ``, false)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	m := res.(map[interface{}]interface{})
+
+	english := m["english"]
+	french := m["french"]
+	german := m["german"]
+	italian := m["italian"]
+	japanese := m["japanese"]
+	korean := m["korean"]
+	chinese := m["chinese"]
+	simplified_chinese := m["simplified_chinese"]
+	traditional_chinese := m["traditional_chinese"]
+	france := m["france"]
+	germany := m["germany"]
+	japan := m["japan"]
+	korea := m["korea"]
+	china := m["china"]
+	prc := m["prc"]
+	taiwan := m["taiwan"]
+	uk := m["uk"]
+	us := m["us"]
+	canada := m["canada"]
+	root := m["root"]
+
+	assert.NotNil(t, english)
+	assert.NotNil(t, french)
+	assert.NotNil(t, german)
+	assert.NotNil(t, italian)
+	assert.NotNil(t, japanese)
+	assert.NotNil(t, korean)
+	assert.NotNil(t, chinese)
+	assert.NotNil(t, simplified_chinese)
+	assert.NotNil(t, traditional_chinese)
+	assert.NotNil(t, france)
+	assert.NotNil(t, germany)
+	assert.NotNil(t, japan)
+	assert.NotNil(t, korea)
+	assert.NotNil(t, china)
+	assert.NotNil(t, prc)
+	assert.NotNil(t, taiwan)
+	assert.NotNil(t, uk)
+	assert.NotNil(t, us)
+	assert.NotNil(t, canada)
+	//assert.NotNil(t, canada_french)
+	assert.NotNil(t, root)
+
+	assert.Equal(t, java_util.ToLocale(java_util.ENGLISH), java_util.GetLocaleFromHandler(english.(*java_util.LocaleHandle)))
+	assert.Equal(t, java_util.ToLocale(java_util.FRENCH), java_util.GetLocaleFromHandler(french.(*java_util.LocaleHandle)))
+	assert.Equal(t, java_util.ToLocale(java_util.GERMANY), java_util.GetLocaleFromHandler(germany.(*java_util.LocaleHandle)))
+	assert.Equal(t, java_util.ToLocale(java_util.ITALIAN), java_util.GetLocaleFromHandler(italian.(*java_util.LocaleHandle)))
+	assert.Equal(t, java_util.ToLocale(java_util.JAPANESE), java_util.GetLocaleFromHandler(japanese.(*java_util.LocaleHandle)))
+	assert.Equal(t, java_util.ToLocale(java_util.KOREAN), java_util.GetLocaleFromHandler(korean.(*java_util.LocaleHandle)))
+	assert.Equal(t, java_util.ToLocale(java_util.CHINESE), java_util.GetLocaleFromHandler(chinese.(*java_util.LocaleHandle)))
+	assert.Equal(t, java_util.ToLocale(java_util.SIMPLIFIED_CHINESE), java_util.GetLocaleFromHandler(simplified_chinese.(*java_util.LocaleHandle)))
+	assert.Equal(t, java_util.ToLocale(java_util.TRADITIONAL_CHINESE), java_util.GetLocaleFromHandler(traditional_chinese.(*java_util.LocaleHandle)))
+	assert.Equal(t, java_util.ToLocale(java_util.FRANCE), java_util.GetLocaleFromHandler(france.(*java_util.LocaleHandle)))
+	assert.Equal(t, java_util.ToLocale(java_util.GERMANY), java_util.GetLocaleFromHandler(germany.(*java_util.LocaleHandle)))
+	assert.Equal(t, java_util.ToLocale(java_util.JAPAN), java_util.GetLocaleFromHandler(japan.(*java_util.LocaleHandle)))
+	assert.Equal(t, java_util.ToLocale(java_util.KOREA), java_util.GetLocaleFromHandler(korea.(*java_util.LocaleHandle)))
+	assert.Equal(t, java_util.ToLocale(java_util.CHINA), java_util.GetLocaleFromHandler(china.(*java_util.LocaleHandle)))
+	assert.Equal(t, java_util.ToLocale(java_util.PRC), java_util.GetLocaleFromHandler(prc.(*java_util.LocaleHandle)))
+	assert.Equal(t, java_util.ToLocale(java_util.TAIWAN), java_util.GetLocaleFromHandler(taiwan.(*java_util.LocaleHandle)))
+	assert.Equal(t, java_util.ToLocale(java_util.UK), java_util.GetLocaleFromHandler(uk.(*java_util.LocaleHandle)))
+	assert.Equal(t, java_util.ToLocale(java_util.US), java_util.GetLocaleFromHandler(us.(*java_util.LocaleHandle)))
+	assert.Equal(t, java_util.ToLocale(java_util.CANADA), java_util.GetLocaleFromHandler(canada.(*java_util.LocaleHandle)))
+	assert.Equal(t, java_util.ToLocale(java_util.ROOT), java_util.GetLocaleFromHandler(root.(*java_util.LocaleHandle)))
+}
diff --git a/test_hessian/src/main/java/test/TestCustomReply.java b/test_hessian/src/main/java/test/TestCustomReply.java
index a3fa27c..08eacaa 100644
--- a/test_hessian/src/main/java/test/TestCustomReply.java
+++ b/test_hessian/src/main/java/test/TestCustomReply.java
@@ -31,14 +31,7 @@ import java.io.OutputStream;
 import java.io.Serializable;
 import java.math.BigDecimal;
 import java.math.BigInteger;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.UUID;
+import java.util.*;
 
 
 public class TestCustomReply {
@@ -433,7 +426,7 @@ public class TestCustomReply {
     public void customReplyObjectJsonObjectBigDecimal() throws Exception {
         JSONObject t = new JSONObject();
         BigDecimal decimal = new BigDecimal("100");
-        t.put("test_BigDecimal",decimal);
+        t.put("test_BigDecimal", decimal);
         output.writeObject(t);
         output.flush();
     }
@@ -529,8 +522,8 @@ public class TestCustomReply {
         map2.put(3L, "c");
         map2.put(4L, "d");
         Map<Integer, BigDecimal> map3 = new HashMap<Integer, BigDecimal>(4);
-        map3.put(5,new BigDecimal("55.55"));
-        map3.put(3,new BigDecimal("33.33"));
+        map3.put(5, new BigDecimal("55.55"));
+        map3.put(3, new BigDecimal("33.33"));
         Map<String, Object> map = new HashMap<String, Object>(4);
         map.put("m1", map1);
         map.put("m2", map2);
@@ -557,7 +550,7 @@ public class TestCustomReply {
         innerMap.put("S", "string");
         items.add(innerMap);
 
-        listMap1.put("items",items);
+        listMap1.put("items", items);
 
         list.add(listMap1);
 
@@ -617,6 +610,45 @@ public class TestCustomReply {
         output.writeObject(map);
         output.flush();
     }
+
+    public void customReplyLocale() throws Exception {
+        Map<String, Object> map = new HashMap<>();
+        map.put("english", Locale.ENGLISH);
+        map.put("french", Locale.FRENCH);
+        map.put("german", Locale.GERMAN);
+        map.put("italian", Locale.ITALIAN);
+        map.put("japanese", Locale.JAPANESE);
+        map.put("korean", Locale.KOREAN);
+        map.put("chinese", Locale.CHINESE);
+        map.put("simplified_chinese", Locale.SIMPLIFIED_CHINESE);
+        map.put("traditional_chinese", Locale.TRADITIONAL_CHINESE);
+        map.put("france", Locale.FRANCE);
+        map.put("germany", Locale.GERMANY);
+        map.put("japan", Locale.JAPAN);
+        map.put("korea", Locale.KOREA);
+        map.put("china", Locale.CHINA);
+        map.put("prc", Locale.PRC);
+        map.put("taiwan", Locale.TAIWAN);
+        map.put("uk", Locale.UK);
+        map.put("us", Locale.US);
+        map.put("canada", Locale.CANADA);
+        map.put("root", Locale.ROOT);
+        // The two objects below is java hessian bug
+        // map.put("italy", Locale.ITALY);
+        // map.put("canada_french", Locale.CANADA_FRENCH);
+        // LocaleHandle
+        output.writeObject(map);
+        output.flush();
+    }
+
+    public void customReplyEnumSet() throws Exception {
+        Map<String, Object> map = new HashMap<>();
+        EnumSet<Locale.Category> enumSet = EnumSet.allOf(Locale.Category.class);
+        map.put("enumset", enumSet);
+        System.out.println(map);
+        output.writeObject(map);
+        output.flush();
+    }
 }
 
 interface Leg {