1// Copyright ©2018 The Gonum Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Package gexf12 implements marshaling and unmarshaling of GEXF1.2 documents.
6//
7// For details of GEXF see https://gephi.org/gexf/format/.
8package gexf12 // import "gonum.org/v1/gonum/graph/formats/gexf12"
9
10import (
11	"bytes"
12	"encoding/xml"
13	"time"
14)
15
16// BUG(kortschak): The namespace for GEFX1.2 is 1.2draft, though it has
17// already been deprecated. There is no specification for 1.3, although
18// it is being used in the wild.
19
20// Content holds a GEFX graph and metadata.
21type Content struct {
22	XMLName xml.Name `xml:"http://www.gexf.net/1.2draft gexf"`
23	Meta    *Meta    `xml:"meta,omitempty"`
24	Graph   Graph    `xml:"graph"`
25	// Version must be "1.2".
26	Version string `xml:"version,attr"`
27	Variant string `xml:"variant,attr,omitempty"`
28}
29
30// Meta holds optional metadata associated with the graph.
31type Meta struct {
32	Creator      string    `xml:"creator,omitempty"`
33	Keywords     string    `xml:"keywords,omitempty"`
34	Description  string    `xml:"description,omitempty"`
35	LastModified time.Time `xml:"lastmodifieddate,attr,omitempty"`
36}
37
38// MarshalXML implements the xml.Marshaler interface.
39func (t *Meta) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
40	type T Meta
41	var layout struct {
42		*T
43		LastModified *xsdDate `xml:"lastmodifieddate,attr,omitempty"`
44	}
45	layout.T = (*T)(t)
46	layout.LastModified = (*xsdDate)(&layout.T.LastModified)
47	return e.EncodeElement(layout, start)
48}
49
50// UnmarshalXML implements the xml.Unmarshaler interface.
51func (t *Meta) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
52	type T Meta
53	var overlay struct {
54		*T
55		LastModified *xsdDate `xml:"lastmodifieddate,attr,omitempty"`
56	}
57	overlay.T = (*T)(t)
58	overlay.LastModified = (*xsdDate)(&overlay.T.LastModified)
59	return d.DecodeElement(&overlay, &start)
60}
61
62// Graph stores the graph nodes, edges, dynamics and visualization data.
63type Graph struct {
64	Attributes []Attributes `xml:"attributes"`
65	Nodes      Nodes        `xml:"nodes"`
66	Edges      Edges        `xml:"edges"`
67	// TimeFormat may be one of "integer", "double", "date" or "dateTime".
68	TimeFormat string `xml:"timeformat,attr,omitempty"`
69	Start      string `xml:"start,attr,omitempty"`
70	StartOpen  string `xml:"startopen,attr,omitempty"`
71	End        string `xml:"end,attr,omitempty"`
72	EndOpen    string `xml:"endopen,attr,omitempty"`
73	// DefaultEdgeType may be one of "directed", "undirected" or "mutual".
74	DefaultEdgeType string `xml:"defaultedgetype,attr,omitempty"`
75	// IDType may be one of "integer" or "string".
76	IDType string `xml:"idtype,attr,omitempty"`
77	// Mode may be "static" or "dynamic".
78	Mode string `xml:"mode,attr,omitempty"`
79}
80
81// Attributes holds a collection of potentially dynamic attributes
82// associated with a graph.
83type Attributes struct {
84	Attributes []Attribute `xml:"attribute,omitempty"`
85	// Class be one of "node" or "edge".
86	Class string `xml:"class,attr"`
87	// Mode may be "static" or "dynamic".
88	Mode      string `xml:"mode,attr,omitempty"`
89	Start     string `xml:"start,attr,omitempty"`
90	StartOpen string `xml:"startopen,attr,omitempty"`
91	End       string `xml:"end,attr,omitempty"`
92	EndOpen   string `xml:"endopen,attr,omitempty"`
93}
94
95// Attribute holds a single graph attribute.
96type Attribute struct {
97	ID    string `xml:"id,attr"`
98	Title string `xml:"title,attr"`
99	// Type may be one of "integer", "long", "double", "float",
100	// "boolean", "liststring", "string", or "anyURI".
101	Type    string `xml:"type,attr"`
102	Default string `xml:"default,omitempty"`
103	Options string `xml:"options,omitempty"`
104}
105
106// Nodes holds a collection of nodes constituting a graph or subgraph.
107type Nodes struct {
108	Count int    `xml:"count,attr,omitempty"`
109	Nodes []Node `xml:"node,omitempty"`
110}
111
112// Node is a single node and its associated attributes.
113type Node struct {
114	ID        string     `xml:"id,attr,omitempty"`
115	Label     string     `xml:"label,attr,omitempty"`
116	AttValues *AttValues `xml:"attvalues"`
117	Spells    *Spells    `xml:"spells"`
118	Nodes     *Nodes     `xml:"nodes"`
119	Edges     *Edges     `xml:"edges"`
120	ParentID  string     `xml:"pid,attr,omitempty"`
121	Parents   *Parents   `xml:"parents"`
122	Color     *Color     `xml:"http://www.gexf.net/1.2draft/viz color"`
123	Position  *Position  `xml:"http://www.gexf.net/1.2draft/viz position"`
124	Size      *Size      `xml:"http://www.gexf.net/1.2draft/viz size"`
125	Shape     *NodeShape `xml:"http://www.gexf.net/1.2draft/viz shape"`
126	Start     string     `xml:"start,attr,omitempty"`
127	StartOpen string     `xml:"startopen,attr,omitempty"`
128	End       string     `xml:"end,attr,omitempty"`
129	EndOpen   string     `xml:"endopen,attr,omitempty"`
130}
131
132// NodeShape holds the visual representation of a node with associated
133// dynamics.
134type NodeShape struct {
135	Spells *Spells `xml:"spells,omitempty"`
136	// Value be one of "disc", "square", "triangle",
137	// "diamond" or "image".
138	Shape     string `xml:"value,attr"`
139	URI       string `xml:"uri,attr,omitempty"`
140	Start     string `xml:"start,attr,omitempty"`
141	StartOpen string `xml:"startopen,attr,omitempty"`
142	End       string `xml:"end,attr,omitempty"`
143	EndOpen   string `xml:"endopen,attr,omitempty"`
144}
145
146// Color represents a node or edge color and its associated dynamics.
147type Color struct {
148	Spells    *Spells `xml:"spells,omitempty"`
149	R         byte    `xml:"r,attr"`
150	G         byte    `xml:"g,attr"`
151	B         byte    `xml:"b,attr"`
152	A         float64 `xml:"a,attr,omitempty"`
153	Start     string  `xml:"start,attr,omitempty"`
154	StartOpen string  `xml:"startopen,attr,omitempty"`
155	End       string  `xml:"end,attr,omitempty"`
156	EndOpen   string  `xml:"endopen,attr,omitempty"`
157}
158
159// Edges holds a collection of edges constituting a graph or subgraph.
160type Edges struct {
161	Count int    `xml:"count,attr,omitempty"`
162	Edges []Edge `xml:"edge,omitempty"`
163}
164
165// Edge is a single edge and its associated attributes.
166type Edge struct {
167	ID        string     `xml:"id,attr,omitempty"`
168	AttValues *AttValues `xml:"attvalues"`
169	Spells    *Spells    `xml:"spells"`
170	Color     *Color     `xml:"http://www.gexf.net/1.2draft/viz color"`
171	Thickness *Thickness `xml:"http://www.gexf.net/1.2draft/viz thickness"`
172	Shape     *Edgeshape `xml:"http://www.gexf.net/1.2draft/viz shape"`
173	Start     string     `xml:"start,attr,omitempty"`
174	StartOpen string     `xml:"startopen,attr,omitempty"`
175	End       string     `xml:"end,attr,omitempty"`
176	EndOpen   string     `xml:"endopen,attr,omitempty"`
177	// Type may be one of directed, undirected, mutual
178	Type   string  `xml:"type,attr,omitempty"`
179	Label  string  `xml:"label,attr,omitempty"`
180	Source string  `xml:"source,attr"`
181	Target string  `xml:"target,attr"`
182	Weight float64 `xml:"weight,attr,omitempty"`
183}
184
185// AttVlues holds a collection of attribute values.
186type AttValues struct {
187	AttValues []AttValue `xml:"attvalue,omitempty"`
188}
189
190// AttValues holds a single attribute value and its associated dynamics.
191type AttValue struct {
192	For       string `xml:"for,attr"`
193	Value     string `xml:"value,attr"`
194	Start     string `xml:"start,attr,omitempty"`
195	StartOpen string `xml:"startopen,attr,omitempty"`
196	End       string `xml:"end,attr,omitempty"`
197	EndOpen   string `xml:"endopen,attr,omitempty"`
198}
199
200// EdgeShape holds the visual representation of an edge with associated
201// dynamics.
202type Edgeshape struct {
203	// Shape be one of solid, dotted, dashed, double
204	Shape     string  `xml:"value,attr"`
205	Spells    *Spells `xml:"spells,omitempty"`
206	Start     string  `xml:"start,attr,omitempty"`
207	StartOpen string  `xml:"startopen,attr,omitempty"`
208	End       string  `xml:"end,attr,omitempty"`
209	EndOpen   string  `xml:"endopen,attr,omitempty"`
210}
211
212// Parents holds parent relationships between nodes in a hierarchical
213// graph.
214type Parents struct {
215	Parents []Parent `xml:"parent,omitempty"`
216}
217
218// Parent is a single parent relationship.
219type Parent struct {
220	For string `xml:"for,attr"`
221}
222
223// Position hold the spatial position of a node and its dynamics.
224type Position struct {
225	X         float64 `xml:"x,attr"`
226	Y         float64 `xml:"y,attr"`
227	Z         float64 `xml:"z,attr"`
228	Spells    *Spells `xml:"spells,omitempty"`
229	Start     string  `xml:"start,attr,omitempty"`
230	StartOpen string  `xml:"startopen,attr,omitempty"`
231	End       string  `xml:"end,attr,omitempty"`
232	EndOpen   string  `xml:"endopen,attr,omitempty"`
233}
234
235// Size hold the visual size of a node and its dynamics.
236type Size struct {
237	Value     float64 `xml:"value,attr"`
238	Spells    *Spells `xml:"http://www.gexf.net/1.2draft/viz spells,omitempty"`
239	Start     string  `xml:"start,attr,omitempty"`
240	StartOpen string  `xml:"startopen,attr,omitempty"`
241	End       string  `xml:"end,attr,omitempty"`
242	EndOpen   string  `xml:"endopen,attr,omitempty"`
243}
244
245// Thickness hold the visual thickness of an edge and its dynamics.
246type Thickness struct {
247	Value     float64 `xml:"value,attr"`
248	Spells    *Spells `xml:"http://www.gexf.net/1.2draft/viz spells,omitempty"`
249	Start     string  `xml:"start,attr,omitempty"`
250	StartOpen string  `xml:"startopen,attr,omitempty"`
251	End       string  `xml:"end,attr,omitempty"`
252	EndOpen   string  `xml:"endopen,attr,omitempty"`
253}
254
255// Spells holds a collection of time dynamics for a graph entity.
256type Spells struct {
257	Spells []Spell `xml:"spell"`
258}
259
260// Spell is a time interval.
261type Spell struct {
262	Start     string `xml:"start,attr,omitempty"`
263	StartOpen string `xml:"startopen,attr,omitempty"`
264	End       string `xml:"end,attr,omitempty"`
265	EndOpen   string `xml:"endopen,attr,omitempty"`
266}
267
268type xsdDate time.Time
269
270func (t *xsdDate) UnmarshalText(text []byte) error {
271	return _unmarshalTime(text, (*time.Time)(t), "2006-01-02")
272}
273
274func (t xsdDate) MarshalText() ([]byte, error) {
275	return []byte((time.Time)(t).Format("2006-01-02")), nil
276}
277
278func (t xsdDate) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
279	if (time.Time)(t).IsZero() {
280		return nil
281	}
282	m, err := t.MarshalText()
283	if err != nil {
284		return err
285	}
286	return e.EncodeElement(m, start)
287}
288
289func (t xsdDate) MarshalXMLAttr(name xml.Name) (xml.Attr, error) {
290	if (time.Time)(t).IsZero() {
291		return xml.Attr{}, nil
292	}
293	m, err := t.MarshalText()
294	return xml.Attr{Name: name, Value: string(m)}, err
295}
296
297func _unmarshalTime(text []byte, t *time.Time, format string) (err error) {
298	s := string(bytes.TrimSpace(text))
299	*t, err = time.Parse(format, s)
300	if _, ok := err.(*time.ParseError); ok {
301		*t, err = time.Parse(format+"Z07:00", s)
302	}
303	return err
304}
305