Files
gliffy2drawio/mapping.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]
}