185 lines
5.1 KiB
Go
185 lines
5.1 KiB
Go
package gliffy2drawio
|
|
|
|
import (
|
|
"bufio"
|
|
"embed"
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
const DefaultTranslationPath = "gliffyTranslation.properties"
|
|
|
|
//go:embed gliffyTranslation.properties
|
|
var embeddedTranslations embed.FS
|
|
|
|
type StencilTranslator struct {
|
|
once sync.Once
|
|
path string
|
|
translations map[string]string
|
|
customFile string
|
|
}
|
|
|
|
func NewStencilTranslator(path string) *StencilTranslator {
|
|
return &StencilTranslator{path: path}
|
|
}
|
|
|
|
// WithCustomMapping allows specifying an optional JSON file (map[string]string) for extra translations.
|
|
func (s *StencilTranslator) WithCustomMapping(jsonPath string) *StencilTranslator {
|
|
s.customFile = jsonPath
|
|
return s
|
|
}
|
|
|
|
func (s *StencilTranslator) Translate(uid, tid string) string {
|
|
s.once.Do(s.load)
|
|
if s.translations == nil {
|
|
return ""
|
|
}
|
|
if v, ok := s.translations[uid]; ok {
|
|
return v
|
|
}
|
|
if tid != "" {
|
|
if v, ok := s.translations[tid]; ok {
|
|
return v
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func (s *StencilTranslator) load() {
|
|
path := s.path
|
|
if path == "" {
|
|
path = DefaultTranslationPath
|
|
}
|
|
var scanner *bufio.Scanner
|
|
if f, err := os.Open(filepath.Clean(path)); err == nil {
|
|
defer f.Close()
|
|
scanner = bufio.NewScanner(f)
|
|
} else if data, err2 := embeddedTranslations.Open(path); err2 == nil {
|
|
defer data.Close()
|
|
scanner = bufio.NewScanner(data)
|
|
} else {
|
|
return
|
|
}
|
|
|
|
s.translations = make(map[string]string)
|
|
for scanner.Scan() {
|
|
line := strings.TrimSpace(scanner.Text())
|
|
if line == "" || strings.HasPrefix(line, "#") {
|
|
continue
|
|
}
|
|
parts := strings.SplitN(line, "=", 2)
|
|
if len(parts) != 2 {
|
|
continue
|
|
}
|
|
s.translations[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1])
|
|
}
|
|
// Hard-coded additions for shapes absent from properties.
|
|
for k, v := range map[string]string{
|
|
"gshape.aws_v3.database.Amazon_RDS_PostgreSQL_instance_light_bg": "mxgraph.aws4.rds_postgresql_instance;fillColor=#3334B9;shadow=0;strokeWidth=2;strokeColor=#333333;opacity=100;html=1;nl2Br=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;spacingLeft=2;spacingRight=0;",
|
|
"gstencil.aws_v3.database.Amazon_RDS_PostgreSQL_instance_light_bg": "mxgraph.aws4.rds_postgresql_instance;fillColor=#3334B9;shadow=0;strokeWidth=2;strokeColor=#333333;opacity=100;html=1;nl2Br=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;spacingLeft=2;spacingRight=0;",
|
|
} {
|
|
if _, ok := s.translations[k]; !ok {
|
|
s.translations[k] = v
|
|
}
|
|
}
|
|
|
|
// Load optional custom JSON mapping file if provided.
|
|
if s.customFile != "" {
|
|
if data, err := os.ReadFile(filepath.Clean(s.customFile)); err == nil {
|
|
var extra map[string]string
|
|
if err := json.Unmarshal(data, &extra); err == nil {
|
|
for k, v := range extra {
|
|
if _, exists := s.translations[k]; !exists {
|
|
s.translations[k] = v
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func DashStyleMapping(value string, width int) string {
|
|
if value == "" {
|
|
return ""
|
|
}
|
|
parts := strings.Split(value, ",")
|
|
for i, p := range parts {
|
|
if f, err := strconv.ParseFloat(strings.TrimSpace(p), 64); err == nil {
|
|
parts[i] = floatToString(f * float64(width))
|
|
}
|
|
}
|
|
return "dashed=1;fixDash=1;dashPattern=" + strings.Join(parts, " ") + ";"
|
|
}
|
|
|
|
var lineMapping = map[string]string{
|
|
"linear": "",
|
|
"orthogonal": "edgeStyle=orthogonal;",
|
|
"quadratic": "curved=1;edgeStyle=orthogonalEdgeStyle;",
|
|
}
|
|
|
|
func LineMapping(style string) string {
|
|
return lineMapping[style]
|
|
}
|
|
|
|
type ArrowStyle struct {
|
|
Name string
|
|
Fill bool
|
|
Size int
|
|
Spacing int
|
|
}
|
|
|
|
func (a ArrowStyle) ToString(start bool) string {
|
|
fill := 0
|
|
if a.Fill {
|
|
fill = 1
|
|
}
|
|
if start {
|
|
return "startArrow=" + a.Name + ";startFill=" + strconv.Itoa(fill) + ";startSize=" + strconv.Itoa(a.Size) + optionalSpacing("sourcePerimeterSpacing", a.Spacing) + ";"
|
|
}
|
|
return "endArrow=" + a.Name + ";endFill=" + strconv.Itoa(fill) + ";endSize=" + strconv.Itoa(a.Size) + optionalSpacing("targetPerimeterSpacing", a.Spacing) + ";"
|
|
}
|
|
|
|
func optionalSpacing(key string, v int) string {
|
|
if v == 0 {
|
|
return ""
|
|
}
|
|
return ";" + key + "=" + strconv.Itoa(v) + ";"
|
|
}
|
|
|
|
var arrowMapping = map[int]ArrowStyle{
|
|
0: {Name: "none", Fill: false, Size: 6},
|
|
1: {Name: "open", Fill: false, Size: 6},
|
|
2: {Name: "block", Fill: true, Size: 6},
|
|
3: {Name: "block", Fill: false, Size: 6},
|
|
4: {Name: "block", Fill: false, Size: 10},
|
|
5: {Name: "diamond", Fill: false, Size: 12},
|
|
6: {Name: "open", Fill: false, Size: 10},
|
|
7: {Name: "diamond", Fill: true, Size: 12},
|
|
8: {Name: "classic", Fill: true, Size: 6},
|
|
9: {Name: "ERzeroToMany", Fill: true, Size: 10},
|
|
10: {Name: "ERoneToMany", Fill: true, Size: 10},
|
|
11: {Name: "ERmandOne", Fill: true, Size: 10},
|
|
12: {Name: "ERone", Fill: true, Size: 10},
|
|
13: {Name: "ERzeroToOne", Fill: true, Size: 10},
|
|
14: {Name: "ERmany", Fill: true, Size: 10},
|
|
15: {Name: "oval", Fill: false, Size: 10, Spacing: 6},
|
|
16: {Name: "dash", Fill: false, Size: 6},
|
|
17: {Name: "block", Fill: true, Size: 6},
|
|
18: {Name: "classic", Fill: true, Size: 6},
|
|
19: {Name: "openAsync", Fill: false, Size: 10},
|
|
}
|
|
|
|
func ArrowMapping(id *int) ArrowStyle {
|
|
if id == nil {
|
|
return arrowMapping[0]
|
|
}
|
|
if a, ok := arrowMapping[*id]; ok {
|
|
return a
|
|
}
|
|
return arrowMapping[0]
|
|
}
|