-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathmodel.go
More file actions
143 lines (124 loc) · 5.22 KB
/
model.go
File metadata and controls
143 lines (124 loc) · 5.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package xmind
import (
"crypto/rand"
"encoding/base32"
"encoding/xml"
"strconv"
"sync/atomic"
)
/*
下面的结构支持从xml和json中解析xmind文件
但只支持生成json的xmind文件,没有做直接生成xml文件的方法
*/
//goland:noinspection SpellCheckingInspection
type (
WorkBook struct {
XMLName xml.Name `xml:"xmap-content"`
Topics []*Topic `json:"sheet" xml:"sheet"`
}
// Topic 定义内容参考xmind官方ts实现,参考如下代码
// https://github.com/xmindltd/xmind-sdk-js/blob/master/src/common/model.ts#L121
Topic struct {
resources map[TopicID]*Topic // 记录所有主题的资源,所有主题共用同一个
parent *Topic // 父节点地址
incr *int // 只用于自增id,生成不重复的默认主题内容
RootTopic *Topic `json:"rootTopic,omitempty" xml:"topic"`
Children *Children `json:"children,omitempty" xml:"children"`
Notes *Notes `json:"notes,omitempty" xml:"notes"`
ID TopicID `json:"id" xml:"id,attr"`
Title string `json:"title" xml:"title"`
Branch string `json:"branch,omitempty" xml:"branch,attr"`
Href string `json:"href,omitempty" xml:"xlink:href,attr"`
StructureClass StructureClass `json:"structureClass,omitempty" xml:"structure-class,attr"`
Style Style `json:"style"`
Labels []string `json:"labels,omitempty" xml:"labels>label"`
}
TopicID string
Style struct {
Properties any `json:"properties"`
Id TopicID `json:"id"`
Type string `json:"type"`
}
Children struct {
Attached []*Topic `json:"attached" xml:"topics>topic"`
}
StructureClass string
Notes struct {
Plain ContentStruct `json:"plain" xml:"plain"`
}
ContentStruct struct {
Content string `json:"content" xml:"content"`
}
)
func (cs *ContentStruct) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
return d.DecodeElement(&cs.Content, &start) // xml和json结构不兼容
}
//goland:noinspection GoUnusedConst,SpellCheckingInspection
const (
ContentJson = "content.json"
ContentXml = "content.xml"
Manifest = "manifest.json"
Metadata = "metadata.json"
Thumbnails = "Thumbnails"
Resources = "resources"
StructMapUnbalanced StructureClass = "org.xmind.ui.map.unbalanced" // 思维导图
StructMap StructureClass = "org.xmind.ui.map" // 平衡图(向下)
StructMapClockwise StructureClass = "org.xmind.ui.map.clockwise" // 平衡图(顺时针)
StructMapAnticlockwise StructureClass = "org.xmind.ui.map.anticlockwise" // 平衡图(逆时针)
StructOrgChartDown StructureClass = "org.xmind.ui.org-chart.down" // 组织结构图(向下)
StructOrgChartUp StructureClass = "org.xmind.ui.org-chart.up" // 组织结构图(向上)
StructTreeRight StructureClass = "org.xmind.ui.tree.right" // 树状图(向右)
StructTreeLeft StructureClass = "org.xmind.ui.tree.left" // 树状图(向左)
StructLogicRight StructureClass = "org.xmind.ui.logic.right" // 逻辑图(向右)
StructLogicLeft StructureClass = "org.xmind.ui.logic.left" // 逻辑图(向左)
StructTimelineHorizontal StructureClass = "org.xmind.ui.timeline.horizontal" // 水平时间轴
StructTimelineVertical StructureClass = "org.xmind.ui.timeline.vertical" // 垂直时间轴
StructFishHoneLeftHeaded StructureClass = "org.xmind.ui.fishbone.leftHeaded" // 鱼骨图(头向左)
StructFishHoneRightHeaded StructureClass = "org.xmind.ui.fishbone.rightHeaded" // 鱼骨图(头向左)
StructSpreadsheet StructureClass = "org.xmind.ui.spreadsheet" // 矩阵(行)
StructSpreadsheetColumn StructureClass = "org.xmind.ui.spreadsheet.column" // 矩阵(列)
)
//goland:noinspection SpellCheckingInspection
var (
objectIDCounter uint32
objectIDbase32 = base32.NewEncoding("123456789abcdefghijklmnopqrstuvw").WithPadding(base32.NoPadding)
)
// topicIdDstLen = objectIDbase32.EncodedLen(topicIdSrcLen) // 提前计算长度
const topicIdSrcLen, topicIdDstLen = 16, 26
func GetId() TopicID {
id := make([]byte, topicIdSrcLen+topicIdDstLen)
_, _ = rand.Reader.Read(id[:topicIdSrcLen])
count := atomic.AddUint32(&objectIDCounter, 1)
for i := 0; i < 4; i++ {
if c := byte(count >> (i * 8)); c > 0 {
id[8-i] = c
}
}
// 16个随机字节(4位为自增id),确保不重复 -> 26个base32编码后字符
objectIDbase32.Encode(id[topicIdSrcLen:], id[:topicIdSrcLen])
return TopicID(id[topicIdSrcLen:])
}
// IsOrdinary 普通节点ID长度固定为26,其他长度均为特殊节点
func (t TopicID) IsOrdinary() bool { return len(t) == topicIdDstLen }
func (t TopicID) MarshalJSON() ([]byte, error) {
if t.IsOrdinary() {
return []byte(`"` + t + `"`), nil
}
return []byte(`"` + GetId() + `"`), nil
}
// CustomIncrId 自定义生成自增数字id方案
func CustomIncrId() func(TopicID) string {
cntId := 0
cntMap := map[TopicID]string{}
// 由于xmind的id生成比较长,这里改为自增数字
return func(id TopicID) string {
s, ok := cntMap[id]
if ok {
return s
}
cntId++
s = strconv.Itoa(cntId)
cntMap[id] = s
return s
}
}