<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"github.com/codegangsta/cli"
	"io/ioutil"
	"os"
)

var colors []string
var debugMode bool = false

type InputNode struct {
	Id      string   `json:"id"`
	Parents []string `json:"parents"`
}

// Types
const (
	PIPE       = iota // 0: |
	MERGE_BACK = iota // 1: ┘
	FORK       = iota // 2: ┐
	MERGE_TO   = iota // 3: ┌
)

type Point struct {
	X    int `json:"x"`
	Y    int `json:"y"`
	Type int `json:"type"`
}

type Path struct {
	Id    string  `json:"id"`
	Path  []Point `json:"path"`
	Color string  `json:"color"`
}

type OutputNode struct {
	Id                string          `json:"id"`
	Parents           []string        `json:"parents"`
	Column            int             `json:"column"`
	ParentsPaths      map[string]Path `json:"-"`
	FinalParentsPaths []Path          `json:"parents_paths"`
	Idx               int             `json:"idx"`
	Children          []string        `json:"-"`
	Color             string          `json:"color"`
	FirstInRow        bool            `json:"-"`
	Debug             []string        `json:"debug,omitempty"`
	NbMoveDown        int             `json:"-"`
}

func (node *OutputNode) Append(parentId string, point Point) {
	tmp := node.ParentsPaths[parentId]
	tmp.Path = append(tmp.Path, point)
	node.ParentsPaths[parentId] = tmp
}

func (node *OutputNode) Remove(parentId string, idx int) {
	tmp := node.ParentsPaths[parentId]
	tmp.Path = append(tmp.Path[:idx], tmp.Path[idx+1:]...)
	node.ParentsPaths[parentId] = tmp
}

func (node *OutputNode) Insert(parentId string, idx int, point Point) {
	tmp := node.ParentsPaths[parentId]
	tmp.Path = append(tmp.Path, Point{})
	copy(tmp.Path[idx+1:], tmp.Path[idx:])
	tmp.Path[idx] = point
	node.ParentsPaths[parentId] = tmp
}

func (node *OutputNode) ColumnDefined() bool {
	return node.Column != -1
}

func (node *OutputNode) HasBiggerParentDefined(index map[string]*OutputNode) bool {
	found := false
	for _, pId := range node.Parents {
		p := index[pId]
		if p.Column > node.Column {
			found = true
			break
		}
	}
	return found
}

func (node *OutputNode) SetPathColor(parentId, color string) {
	tmp := node.ParentsPaths[parentId]
	tmp.Color = color
	node.ParentsPaths[parentId] = tmp
}

func serializeOutput(out []*OutputNode) ([]byte, error) {
	for _, node := range out {
		for parentId, path := range node.ParentsPaths {
			node.FinalParentsPaths = append(node.FinalParentsPaths, Path{parentId, path.Path, path.Color})
		}
	}
	treeBytes, err := json.Marshal(&out)
	return treeBytes, err
}

func getInputNodesFromJson(inputJson string) (nodes []InputNode, err error) {
	if err = json.Unmarshal([]byte(inputJson), &nodes); err != nil {
		return
	}
	return
}

func initNodes(inputNodes []InputNode) []*OutputNode {
	out := make([]*OutputNode, 0)
	for idx, node := range inputNodes {
		newNode := OutputNode{}
		newNode.Id = node.Id
		newNode.Parents = node.Parents
		newNode.Column = -1
		newNode.ParentsPaths = make(map[string]Path)
		newNode.FinalParentsPaths = make([]Path, 0)
		newNode.Idx = idx
		newNode.Children = make([]string, 0)
		newNode.Debug = make([]string, 0)
		newNode.NbMoveDown = 0
		out = append(out, &newNode)
	}
	return out
}

func initIndex(nodes []*OutputNode) map[string]*OutputNode {
	index := make(map[string]*OutputNode)
	for _, node := range nodes {
		index[node.Id] = node
	}
	return index
}

func initChildren(nodes []*OutputNode, index map[string]*OutputNode) {
	for _, node := range nodes {
		for _, parentId := range node.Parents {
			index[parentId].Children = append(index[parentId].Children, node.Id)
		}
	}
}

func setColumns(nodes []*OutputNode, index map[string]*OutputNode) {
	nextColumn := 0
	for _, node := range nodes {
		if !node.ColumnDefined() {
			node.Column = nextColumn
			if debugMode {
				node.Debug = append(node.Debug, fmt.Sprintf("Column set to %d", nextColumn))
			}
			node.Color, colors = colors[0], colors[1:]
			nextColumn++
		}

		nbNodesMergingBack := 0
		for _, childId := range node.Children {
			child := index[childId]
			isNodeMerging := child.ParentsPaths[node.Id].Path[len(child.ParentsPaths[node.Id].Path)-2].Type == MERGE_TO
			if (node.Column+node.NbMoveDown) < child.Column && !isNodeMerging {
				nbNodesMergingBack++
			}
		}

		for _, childId := range node.Children {
			child := index[childId]
			isNodeMerging := child.ParentsPaths[node.Id].Path[len(child.ParentsPaths[node.Id].Path)-2].Type == MERGE_TO
			if (node.Column+node.NbMoveDown) < child.Column && !isNodeMerging {
				nextColumn--

				if child.Parents[0] != node.Id || len(child.Parents) <= 1 {
					if !child.FirstInRow {
						child.SetPathColor(node.Id, child.Color)
					}
					colors = append(colors[:1], append([]string{child.Color}, colors[1:]...)...)

					// Insert before the last element
					pos := len(child.ParentsPaths[node.Id].Path) - 1
					point := Point{child.ParentsPaths[node.Id].Path[pos-1].X, node.Idx, MERGE_BACK}
					child.Insert(node.Id, pos, point)

					// Nodes that are following the current node
					for followingNodeIdx, followingNode := range nodes {
						if followingNodeIdx > node.Idx {
							// Following nodes that have a child before the current node
							for _, followingNodeChildId := range followingNode.Children {
								followingNodeChild := index[followingNodeChildId]
								if followingNodeChild.Idx < node.Idx {
									// Following node child has a path that is higher than the current path being merged
									if followingNodeChild.ParentsPaths[followingNode.Id].Path[len(followingNodeChild.ParentsPaths[followingNode.Id].Path)-2].X > child.ParentsPaths[node.Id].Path[len(child.ParentsPaths[node.Id].Path)-2].X {
										idxRemove := len(followingNodeChild.ParentsPaths[followingNode.Id].Path) - 1
										if idxRemove < 0 {
											continue
										}
										if followingNode.Column > child.ParentsPaths[node.Id].Path[len(child.ParentsPaths[node.Id].Path)-2].X {
											followingNodeChild.Remove(followingNode.Id, idxRemove)
											followingNodeChild.Append(followingNode.Id, Point{followingNodeChild.ParentsPaths[followingNode.Id].Path[idxRemove-1].X, node.Idx, MERGE_BACK})
											followingNodeChild.Append(followingNode.Id, Point{followingNode.Column - 1 - (nbNodesMergingBack - 1), node.Idx, PIPE})
											followingNodeChild.Append(followingNode.Id, Point{followingNode.Column - 1, followingNode.Idx, PIPE})
										} else {
											tmp := followingNodeChild.ParentsPaths[followingNode.Id].Path[idxRemove-1].X
											followingNodeChild.Remove(followingNode.Id, idxRemove)
											followingNodeChild.Append(followingNode.Id, Point{tmp, node.Idx, MERGE_BACK})
											followingNodeChild.Append(followingNode.Id, Point{tmp - 1 - (nbNodesMergingBack - 1), node.Idx, PIPE})
											followingNodeChild.Append(followingNode.Id, Point{tmp - 1 - (nbNodesMergingBack - 1), followingNode.Idx, MERGE_BACK})
											followingNodeChild.Append(followingNode.Id, Point{followingNode.Column, followingNode.Idx, PIPE})
										}
									}
								}
							}
							if followingNode.Column > child.ParentsPaths[node.Id].Path[len(child.ParentsPaths[node.Id].Path)-2].X {
								followingNode.Column--
								followingNode.NbMoveDown++
								if debugMode {
									followingNode.Debug = append(followingNode.Debug, fmt.Sprintf("Node moved down, %s -> %s", child.Id, node.Id))
								}
							}
						}
					}
				}
			}
		}

		for parentIdx, parentId := range node.Parents {
			parent := index[parentId]

			node.Append(parent.Id, Point{node.Column, node.Idx, PIPE})

			if !parent.ColumnDefined() {
				if parentIdx == 0 || (parentIdx == 1 && index[node.Parents[0]].Column < node.Column) {
					parent.Column = node.Column
					if debugMode {
						parent.Debug = append(parent.Debug, fmt.Sprintf("Column set to %d", node.Column))
					}
					parent.Color = node.Color
					node.SetPathColor(parent.Id, parent.Color)
				} else {
					parent.Column = nextColumn
					if debugMode {
						parent.Debug = append(parent.Debug, fmt.Sprintf("Column set to %d", nextColumn))
					}
					parent.Color, colors = colors[0], colors[1:]
					node.Append(parent.Id, Point{parent.Column, node.Idx, FORK})
					node.SetPathColor(parent.Id, parent.Color)
					node.FirstInRow = true
					nextColumn++
				}
			} else if parent.ColumnDefined() {
				if node.Column < parent.Column && parentIdx == 0 {
					for _, childId := range parent.Children {
						child := index[childId]
						idxRemove := len(child.ParentsPaths[parent.Id].Path) - 1
						if idxRemove > 0 {
							if child.ParentsPaths[parent.Id].Path[idxRemove].Type != FORK {
								child.Remove(parent.Id, idxRemove)
							}
							pos := len(child.ParentsPaths[parent.Id].Path) - 1
							child.Append(parent.Id, Point{child.ParentsPaths[parent.Id].Path[pos].X, parent.Idx, MERGE_BACK})
							child.Append(parent.Id, Point{node.Column, parent.Idx, PIPE})
						}
					}
					parent.Column = node.Column
					if debugMode {
						parent.Debug = append(parent.Debug, fmt.Sprintf("Column reset to %d", node.Column))
					}
					parent.Color = node.Color
					node.SetPathColor(parent.Id, node.Color)
				} else if node.Column < parent.Column && parentIdx > 0 {
					node.Append(parent.Id, Point{parent.Column, node.Idx, FORK})
					node.SetPathColor(parent.Id, parent.Color)
				} else if node.Column > parent.Column {
					if len(node.Parents) > 1 {
						if node.HasBiggerParentDefined(index) {
							node.Append(parent.Id, Point{node.Column, parent.Idx, MERGE_BACK})
							node.SetPathColor(parent.Id, node.Color)
						} else {
							node.Append(parent.Id, Point{parent.Column, node.Idx, MERGE_TO})
							node.SetPathColor(parent.Id, parent.Color)
						}
					}
				}
			}

			node.Append(parent.Id, Point{parent.Column, parent.Idx, PIPE})

		}
	}

	// Deduplicate path nodes
	for _, node := range nodes {
		for pathIdx, path := range node.ParentsPaths {
			previousPoint := Point{-1, -1, -1}
			for pointIdx, point := range path.Path {
				if point.X == previousPoint.X && point.Y == previousPoint.Y && point.Type == previousPoint.Type {
					tmp := node.ParentsPaths[pathIdx]
					tmp.Path = append(tmp.Path[:pointIdx], tmp.Path[pointIdx+1:]...)
					node.ParentsPaths[pathIdx] = tmp
				}
				previousPoint = point
			}
		}
	}
}

func buildTree(inputNodes []InputNode, myColors []string) ([]*OutputNode, error) {
	colors = myColors
	var nodes []*OutputNode = initNodes(inputNodes)
	var index map[string]*OutputNode = initIndex(nodes)

	initChildren(nodes, index)
	setColumns(nodes, index)

	return nodes, nil
}

func BuildTreeJson(inputJson string, myColors []string) (tree string, err error) {
	nodes, err := getInputNodesFromJson(inputJson)
	if err != nil {
		return
	}

	out, err := buildTree(nodes, myColors)
	if err != nil {
		return
	}

	treeBytes, err := serializeOutput(out)
	if err != nil {
		return
	}
	tree = string(treeBytes)
	return
}

func bootstrap(c *cli.Context) {
	var inputJson string
	jsonFlag := c.String("json")
	fileFlag := c.String("file")
	debugMode = c.Bool("debug")

	if jsonFlag != "" {
		inputJson = jsonFlag
	} else if fileFlag != "" {
		bytes, err := ioutil.ReadFile(fileFlag)
		if err != nil {
			fmt.Println(err)
			return
		}
		inputJson = string(bytes)
	} else {
		cli.ShowAppHelp(c)
		return
	}

	myColors := []string{"#5aa1be", "#c065b8", "#c0ab5f", "#59bc95", "#c0615b", "#7a63be", "#73bb5e", "#6ee585", "#7088e8", "#eb77a3"}

	out, err := BuildTreeJson(inputJson, myColors)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(out)
}

func main() {
	var authors []cli.Author
	// Collaborators, add your name here :)
	authors = append(authors, cli.Author{"Alain Gilbert", "alain.gilbert.15@gmail.com"})

	app := cli.NewApp()
	app.Authors = authors
	app.Version = "0.0.0"
	app.Name = "git2graph"
	app.Usage = "Take a git tree, make a graph structure"
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "f, file",
			Usage: "File",
		},
		cli.StringFlag{
			Name:  "j, json",
			Usage: "Json input",
		},
		cli.BoolFlag{
			Name:  "d, debug",
			Usage: "Debug mode",
		},
	}
	app.Action = bootstrap
	app.Run(os.Args)
}
<commit_msg>change color order<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"github.com/codegangsta/cli"
	"io/ioutil"
	"os"
)

var colors []string
var debugMode bool = false

type InputNode struct {
	Id      string   `json:"id"`
	Parents []string `json:"parents"`
}

// Types
const (
	PIPE       = iota // 0: |
	MERGE_BACK = iota // 1: ┘
	FORK       = iota // 2: ┐
	MERGE_TO   = iota // 3: ┌
)

type Point struct {
	X    int `json:"x"`
	Y    int `json:"y"`
	Type int `json:"type"`
}

type Path struct {
	Id    string  `json:"id"`
	Path  []Point `json:"path"`
	Color string  `json:"color"`
}

type OutputNode struct {
	Id                string          `json:"id"`
	Parents           []string        `json:"parents"`
	Column            int             `json:"column"`
	ParentsPaths      map[string]Path `json:"-"`
	FinalParentsPaths []Path          `json:"parents_paths"`
	Idx               int             `json:"idx"`
	Children          []string        `json:"-"`
	Color             string          `json:"color"`
	FirstInRow        bool            `json:"-"`
	Debug             []string        `json:"debug,omitempty"`
	NbMoveDown        int             `json:"-"`
}

func (node *OutputNode) Append(parentId string, point Point) {
	tmp := node.ParentsPaths[parentId]
	tmp.Path = append(tmp.Path, point)
	node.ParentsPaths[parentId] = tmp
}

func (node *OutputNode) Remove(parentId string, idx int) {
	tmp := node.ParentsPaths[parentId]
	tmp.Path = append(tmp.Path[:idx], tmp.Path[idx+1:]...)
	node.ParentsPaths[parentId] = tmp
}

func (node *OutputNode) Insert(parentId string, idx int, point Point) {
	tmp := node.ParentsPaths[parentId]
	tmp.Path = append(tmp.Path, Point{})
	copy(tmp.Path[idx+1:], tmp.Path[idx:])
	tmp.Path[idx] = point
	node.ParentsPaths[parentId] = tmp
}

func (node *OutputNode) ColumnDefined() bool {
	return node.Column != -1
}

func (node *OutputNode) HasBiggerParentDefined(index map[string]*OutputNode) bool {
	found := false
	for _, pId := range node.Parents {
		p := index[pId]
		if p.Column > node.Column {
			found = true
			break
		}
	}
	return found
}

func (node *OutputNode) SetPathColor(parentId, color string) {
	tmp := node.ParentsPaths[parentId]
	tmp.Color = color
	node.ParentsPaths[parentId] = tmp
}

func serializeOutput(out []*OutputNode) ([]byte, error) {
	for _, node := range out {
		for parentId, path := range node.ParentsPaths {
			node.FinalParentsPaths = append(node.FinalParentsPaths, Path{parentId, path.Path, path.Color})
		}
	}
	treeBytes, err := json.Marshal(&out)
	return treeBytes, err
}

func getInputNodesFromJson(inputJson string) (nodes []InputNode, err error) {
	if err = json.Unmarshal([]byte(inputJson), &nodes); err != nil {
		return
	}
	return
}

func initNodes(inputNodes []InputNode) []*OutputNode {
	out := make([]*OutputNode, 0)
	for idx, node := range inputNodes {
		newNode := OutputNode{}
		newNode.Id = node.Id
		newNode.Parents = node.Parents
		newNode.Column = -1
		newNode.ParentsPaths = make(map[string]Path)
		newNode.FinalParentsPaths = make([]Path, 0)
		newNode.Idx = idx
		newNode.Children = make([]string, 0)
		newNode.Debug = make([]string, 0)
		newNode.NbMoveDown = 0
		out = append(out, &newNode)
	}
	return out
}

func initIndex(nodes []*OutputNode) map[string]*OutputNode {
	index := make(map[string]*OutputNode)
	for _, node := range nodes {
		index[node.Id] = node
	}
	return index
}

func initChildren(nodes []*OutputNode, index map[string]*OutputNode) {
	for _, node := range nodes {
		for _, parentId := range node.Parents {
			index[parentId].Children = append(index[parentId].Children, node.Id)
		}
	}
}

func setColumns(nodes []*OutputNode, index map[string]*OutputNode) {
	nextColumn := 0
	for _, node := range nodes {
		if !node.ColumnDefined() {
			node.Column = nextColumn
			if debugMode {
				node.Debug = append(node.Debug, fmt.Sprintf("Column set to %d", nextColumn))
			}
			node.Color, colors = colors[0], colors[1:]
			nextColumn++
		}

		nbNodesMergingBack := 0
		for _, childId := range node.Children {
			child := index[childId]
			isNodeMerging := child.ParentsPaths[node.Id].Path[len(child.ParentsPaths[node.Id].Path)-2].Type == MERGE_TO
			if (node.Column+node.NbMoveDown) < child.Column && !isNodeMerging {
				nbNodesMergingBack++
			}
		}

		for _, childId := range node.Children {
			child := index[childId]
			isNodeMerging := child.ParentsPaths[node.Id].Path[len(child.ParentsPaths[node.Id].Path)-2].Type == MERGE_TO
			if (node.Column+node.NbMoveDown) < child.Column && !isNodeMerging {
				nextColumn--

				if child.Parents[0] != node.Id || len(child.Parents) <= 1 {
					if !child.FirstInRow {
						child.SetPathColor(node.Id, child.Color)
					}
					colors = append(colors[:1], append([]string{child.Color}, colors[1:]...)...)

					// Insert before the last element
					pos := len(child.ParentsPaths[node.Id].Path) - 1
					point := Point{child.ParentsPaths[node.Id].Path[pos-1].X, node.Idx, MERGE_BACK}
					child.Insert(node.Id, pos, point)

					// Nodes that are following the current node
					for followingNodeIdx, followingNode := range nodes {
						if followingNodeIdx > node.Idx {
							// Following nodes that have a child before the current node
							for _, followingNodeChildId := range followingNode.Children {
								followingNodeChild := index[followingNodeChildId]
								if followingNodeChild.Idx < node.Idx {
									// Following node child has a path that is higher than the current path being merged
									if followingNodeChild.ParentsPaths[followingNode.Id].Path[len(followingNodeChild.ParentsPaths[followingNode.Id].Path)-2].X > child.ParentsPaths[node.Id].Path[len(child.ParentsPaths[node.Id].Path)-2].X {
										idxRemove := len(followingNodeChild.ParentsPaths[followingNode.Id].Path) - 1
										if idxRemove < 0 {
											continue
										}
										if followingNode.Column > child.ParentsPaths[node.Id].Path[len(child.ParentsPaths[node.Id].Path)-2].X {
											followingNodeChild.Remove(followingNode.Id, idxRemove)
											followingNodeChild.Append(followingNode.Id, Point{followingNodeChild.ParentsPaths[followingNode.Id].Path[idxRemove-1].X, node.Idx, MERGE_BACK})
											followingNodeChild.Append(followingNode.Id, Point{followingNode.Column - 1 - (nbNodesMergingBack - 1), node.Idx, PIPE})
											followingNodeChild.Append(followingNode.Id, Point{followingNode.Column - 1, followingNode.Idx, PIPE})
										} else {
											tmp := followingNodeChild.ParentsPaths[followingNode.Id].Path[idxRemove-1].X
											followingNodeChild.Remove(followingNode.Id, idxRemove)
											followingNodeChild.Append(followingNode.Id, Point{tmp, node.Idx, MERGE_BACK})
											followingNodeChild.Append(followingNode.Id, Point{tmp - 1 - (nbNodesMergingBack - 1), node.Idx, PIPE})
											followingNodeChild.Append(followingNode.Id, Point{tmp - 1 - (nbNodesMergingBack - 1), followingNode.Idx, MERGE_BACK})
											followingNodeChild.Append(followingNode.Id, Point{followingNode.Column, followingNode.Idx, PIPE})
										}
									}
								}
							}
							if followingNode.Column > child.ParentsPaths[node.Id].Path[len(child.ParentsPaths[node.Id].Path)-2].X {
								followingNode.Column--
								followingNode.NbMoveDown++
								if debugMode {
									followingNode.Debug = append(followingNode.Debug, fmt.Sprintf("Node moved down, %s -> %s", child.Id, node.Id))
								}
							}
						}
					}
				}
			}
		}

		for parentIdx, parentId := range node.Parents {
			parent := index[parentId]

			node.Append(parent.Id, Point{node.Column, node.Idx, PIPE})

			if !parent.ColumnDefined() {
				if parentIdx == 0 || (parentIdx == 1 && index[node.Parents[0]].Column < node.Column) {
					parent.Column = node.Column
					if debugMode {
						parent.Debug = append(parent.Debug, fmt.Sprintf("Column set to %d", node.Column))
					}
					parent.Color = node.Color
					node.SetPathColor(parent.Id, parent.Color)
				} else {
					parent.Column = nextColumn
					if debugMode {
						parent.Debug = append(parent.Debug, fmt.Sprintf("Column set to %d", nextColumn))
					}
					parent.Color, colors = colors[0], colors[1:]
					node.Append(parent.Id, Point{parent.Column, node.Idx, FORK})
					node.SetPathColor(parent.Id, parent.Color)
					node.FirstInRow = true
					nextColumn++
				}
			} else if parent.ColumnDefined() {
				if node.Column < parent.Column && parentIdx == 0 {
					for _, childId := range parent.Children {
						child := index[childId]
						idxRemove := len(child.ParentsPaths[parent.Id].Path) - 1
						if idxRemove > 0 {
							if child.ParentsPaths[parent.Id].Path[idxRemove].Type != FORK {
								child.Remove(parent.Id, idxRemove)
							}
							pos := len(child.ParentsPaths[parent.Id].Path) - 1
							child.Append(parent.Id, Point{child.ParentsPaths[parent.Id].Path[pos].X, parent.Idx, MERGE_BACK})
							child.Append(parent.Id, Point{node.Column, parent.Idx, PIPE})
						}
					}
					parent.Column = node.Column
					if debugMode {
						parent.Debug = append(parent.Debug, fmt.Sprintf("Column reset to %d", node.Column))
					}
					parent.Color = node.Color
					node.SetPathColor(parent.Id, node.Color)
				} else if node.Column < parent.Column && parentIdx > 0 {
					node.Append(parent.Id, Point{parent.Column, node.Idx, FORK})
					node.SetPathColor(parent.Id, parent.Color)
				} else if node.Column > parent.Column {
					if len(node.Parents) > 1 {
						if node.HasBiggerParentDefined(index) {
							node.Append(parent.Id, Point{node.Column, parent.Idx, MERGE_BACK})
							node.SetPathColor(parent.Id, node.Color)
						} else {
							node.Append(parent.Id, Point{parent.Column, node.Idx, MERGE_TO})
							node.SetPathColor(parent.Id, parent.Color)
						}
					}
				}
			}

			node.Append(parent.Id, Point{parent.Column, parent.Idx, PIPE})

		}
	}

	// Deduplicate path nodes
	for _, node := range nodes {
		for pathIdx, path := range node.ParentsPaths {
			previousPoint := Point{-1, -1, -1}
			for pointIdx, point := range path.Path {
				if point.X == previousPoint.X && point.Y == previousPoint.Y && point.Type == previousPoint.Type {
					tmp := node.ParentsPaths[pathIdx]
					tmp.Path = append(tmp.Path[:pointIdx], tmp.Path[pointIdx+1:]...)
					node.ParentsPaths[pathIdx] = tmp
				}
				previousPoint = point
			}
		}
	}
}

func buildTree(inputNodes []InputNode, myColors []string) ([]*OutputNode, error) {
	colors = myColors
	var nodes []*OutputNode = initNodes(inputNodes)
	var index map[string]*OutputNode = initIndex(nodes)

	initChildren(nodes, index)
	setColumns(nodes, index)

	return nodes, nil
}

func BuildTreeJson(inputJson string, myColors []string) (tree string, err error) {
	nodes, err := getInputNodesFromJson(inputJson)
	if err != nil {
		return
	}

	out, err := buildTree(nodes, myColors)
	if err != nil {
		return
	}

	treeBytes, err := serializeOutput(out)
	if err != nil {
		return
	}
	tree = string(treeBytes)
	return
}

func bootstrap(c *cli.Context) {
	var inputJson string
	jsonFlag := c.String("json")
	fileFlag := c.String("file")
	debugMode = c.Bool("debug")

	if jsonFlag != "" {
		inputJson = jsonFlag
	} else if fileFlag != "" {
		bytes, err := ioutil.ReadFile(fileFlag)
		if err != nil {
			fmt.Println(err)
			return
		}
		inputJson = string(bytes)
	} else {
		cli.ShowAppHelp(c)
		return
	}

	myColors := []string{"#5aa1be", "#c065b8", "#c0ab5f", "#59bc95", "#7a63be", "#c0615b", "#73bb5e", "#6ee585", "#7088e8", "#eb77a3"}

	out, err := BuildTreeJson(inputJson, myColors)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(out)
}

func main() {
	var authors []cli.Author
	// Collaborators, add your name here :)
	authors = append(authors, cli.Author{"Alain Gilbert", "alain.gilbert.15@gmail.com"})

	app := cli.NewApp()
	app.Authors = authors
	app.Version = "0.0.0"
	app.Name = "git2graph"
	app.Usage = "Take a git tree, make a graph structure"
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "f, file",
			Usage: "File",
		},
		cli.StringFlag{
			Name:  "j, json",
			Usage: "Json input",
		},
		cli.BoolFlag{
			Name:  "d, debug",
			Usage: "Debug mode",
		},
	}
	app.Action = bootstrap
	app.Run(os.Args)
}
<|endoftext|>


<commit_before>/*
Copyright 2020 The Kubernetes Authors.

Licensed 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 deployer

import (
	"crypto/md5"
	"errors"
	"fmt"
	"os"
	"path"
	"path/filepath"
	"strings"

	"k8s.io/klog/v2"
	"k8s.io/kops/tests/e2e/pkg/kops"
	"k8s.io/kops/tests/e2e/pkg/target"
)

func (d *deployer) init() error {
	var err error
	d.doInit.Do(func() { err = d.initialize() })
	return err
}

// initialize should only be called by init(), behind a sync.Once
func (d *deployer) initialize() error {
	if d.commonOptions.ShouldBuild() {
		if err := d.verifyBuildFlags(); err != nil {
			return fmt.Errorf("init failed to check build flags: %v", err)
		}
	}
	if d.commonOptions.ShouldUp() || d.commonOptions.ShouldDown() {
		if err := d.verifyKopsFlags(); err != nil {
			return fmt.Errorf("init failed to check kops flags: %v", err)
		}
	}
	if d.commonOptions.ShouldUp() {
		if err := d.verifyUpFlags(); err != nil {
			return fmt.Errorf("init failed to check up flags: %v", err)
		}
	}
	if d.KopsVersionMarker != "" {
		binaryPath, baseURL, err := kops.DownloadKops(d.KopsVersionMarker, d.KopsBinaryPath)
		if err != nil {
			return fmt.Errorf("init failed to download kops from url: %v", err)
		}
		d.KopsBinaryPath = binaryPath
		d.KopsBaseURL = baseURL
	}
	// These environment variables are defined by the "preset-aws-ssh" prow preset
	// https://github.com/kubernetes/test-infra/blob/3d3b325c98b739b526ba5d93ce21c90a05e1f46d/config/prow/config.yaml#L653-L670
	if d.SSHPrivateKeyPath == "" {
		d.SSHPrivateKeyPath = os.Getenv("AWS_SSH_PRIVATE_KEY_FILE")
	}
	if d.SSHPublicKeyPath == "" {
		d.SSHPublicKeyPath = os.Getenv("AWS_SSH_PUBLIC_KEY_FILE")
	}
	if d.SSHUser == "" {
		d.SSHUser = os.Getenv("KUBE_SSH_USER")
	}
	if d.TerraformVersion != "" {
		t, err := target.NewTerraform(d.TerraformVersion)
		if err != nil {
			return err
		}
		d.terraform = t
	}
	return nil
}

// verifyKopsFlags ensures common fields are set for kops commands
func (d *deployer) verifyKopsFlags() error {
	if d.ClusterName == "" {
		name, err := defaultClusterName(d.CloudProvider)
		if err != nil {
			return err
		}
		klog.Info("Using cluster name ", d.ClusterName)
		d.ClusterName = name
	}

	if d.KopsBinaryPath == "" && d.KopsVersionMarker == "" {
		if ws := os.Getenv("WORKSPACE"); ws != "" {
			d.KopsBinaryPath = path.Join(ws, "kops")
		} else {
			return errors.New("missing required --kops-binary-path when --kops-version-marker is not used")
		}
	}

	switch d.CloudProvider {
	case "aws":
	case "gce":
	case "digitalocean":
	default:
		return errors.New("unsupported --cloud-provider value")
	}

	if d.StateStore == "" {
		d.StateStore = stateStore(d.CloudProvider)
	}

	return nil
}

// env returns a list of environment variables passed to the kops binary
func (d *deployer) env() []string {
	vars := d.Env
	vars = append(vars, []string{
		fmt.Sprintf("PATH=%v", os.Getenv("PATH")),
		fmt.Sprintf("HOME=%v", os.Getenv("HOME")),
		fmt.Sprintf("KOPS_STATE_STORE=%v", d.StateStore),
		fmt.Sprintf("KOPS_FEATURE_FLAGS=%v", d.featureFlags()),
		"KOPS_RUN_TOO_NEW_VERSION=1",
	}...)
	if d.CloudProvider == "aws" {
		// Pass through some env vars if set
		for _, k := range []string{"AWS_PROFILE", "AWS_SHARED_CREDENTIALS_FILE"} {
			v := os.Getenv(k)
			if v != "" {
				vars = append(vars, k+"="+v)
			}
		}
	}
	if d.KopsBaseURL != "" {
		vars = append(vars, fmt.Sprintf("KOPS_BASE_URL=%v", d.KopsBaseURL))
	} else if baseURL := os.Getenv("KOPS_BASE_URL"); baseURL != "" {
		vars = append(vars, fmt.Sprintf("KOPS_BASE_URL=%v", os.Getenv("KOPS_BASE_URL")))
	}
	return vars
}

// featureFlags returns the kops feature flags to set
func (d *deployer) featureFlags() string {
	ff := []string{
		"+SpecOverrideFlag",
		"+AlphaAllowGCE",
	}
	val := strings.Join(ff, ",")
	for _, env := range d.Env {
		e := strings.Split(env, "=")
		if e[0] == "KOPS_FEATURE_FLAGS" && len(e) > 1 {
			val = fmt.Sprintf("%v,%v", val, e[1])
		}
	}
	return val
}

// defaultClusterName returns a kops cluster name to use when ClusterName is not set
func defaultClusterName(cloudProvider string) (string, error) {
	jobName := os.Getenv("JOB_NAME")
	buildID := os.Getenv("BUILD_ID")
	if jobName == "" || buildID == "" {
		return "", errors.New("JOB_NAME, and BUILD_ID env vars are required when --cluster-name is not set")
	}

	buildIDHash := fmt.Sprintf("%x", md5.Sum([]byte(buildID)))
	jobHash := fmt.Sprintf("%x", md5.Sum([]byte(jobName)))

	var suffix string
	switch cloudProvider {
	case "aws":
		suffix = "test-cncf-aws.k8s.io"
	default:
		suffix = "k8s.local"
	}

	return fmt.Sprintf("e2e-%v-%v.%v", buildIDHash[:10], jobHash[:5], suffix), nil
}

// stateStore returns the kops state store to use
// defaulting to values used in prow jobs
func stateStore(cloudProvider string) string {
	ss := os.Getenv("KOPS_STATE_STORE")
	if ss == "" {
		switch cloudProvider {
		case "aws":
			ss = "s3://k8s-kops-prow"
		case "gce":
			ss = "gs://k8s-kops-gce"
		}
	}
	return ss
}

// the default is $ARTIFACTS if set, otherwise ./_artifacts
// constructed as an absolute path to help the ginkgo tester because
// for some reason it needs an absolute path to the kubeconfig
func defaultArtifactsDir() (string, error) {
	if path, set := os.LookupEnv("ARTIFACTS"); set {
		absPath, err := filepath.Abs(path)
		if err != nil {
			return "", fmt.Errorf("failed to convert filepath from $ARTIFACTS (%s) to absolute path: %s", path, err)
		}
		return absPath, nil
	}

	absPath, err := filepath.Abs("_artifacts")
	if err != nil {
		return "", fmt.Errorf("when constructing default artifacts dir, failed to get absolute path: %s", err)
	}
	return absPath, nil
}
<commit_msg>Kubetest2 - Add GCE default SSH key values from prow jobs<commit_after>/*
Copyright 2020 The Kubernetes Authors.

Licensed 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 deployer

import (
	"crypto/md5"
	"errors"
	"fmt"
	"os"
	"path"
	"path/filepath"
	"strings"

	"k8s.io/klog/v2"
	"k8s.io/kops/tests/e2e/pkg/kops"
	"k8s.io/kops/tests/e2e/pkg/target"
)

func (d *deployer) init() error {
	var err error
	d.doInit.Do(func() { err = d.initialize() })
	return err
}

// initialize should only be called by init(), behind a sync.Once
func (d *deployer) initialize() error {
	if d.commonOptions.ShouldBuild() {
		if err := d.verifyBuildFlags(); err != nil {
			return fmt.Errorf("init failed to check build flags: %v", err)
		}
	}
	if d.commonOptions.ShouldUp() || d.commonOptions.ShouldDown() {
		if err := d.verifyKopsFlags(); err != nil {
			return fmt.Errorf("init failed to check kops flags: %v", err)
		}
	}
	if d.commonOptions.ShouldUp() {
		if err := d.verifyUpFlags(); err != nil {
			return fmt.Errorf("init failed to check up flags: %v", err)
		}
	}
	if d.KopsVersionMarker != "" {
		binaryPath, baseURL, err := kops.DownloadKops(d.KopsVersionMarker, d.KopsBinaryPath)
		if err != nil {
			return fmt.Errorf("init failed to download kops from url: %v", err)
		}
		d.KopsBinaryPath = binaryPath
		d.KopsBaseURL = baseURL
	}
	switch d.CloudProvider {
	case "aws":
		// These environment variables are defined by the "preset-aws-ssh" prow preset
		// https://github.com/kubernetes/test-infra/blob/3d3b325c98b739b526ba5d93ce21c90a05e1f46d/config/prow/config.yaml#L653-L670
		if d.SSHPrivateKeyPath == "" {
			d.SSHPrivateKeyPath = os.Getenv("AWS_SSH_PRIVATE_KEY_FILE")
		}
		if d.SSHPublicKeyPath == "" {
			d.SSHPublicKeyPath = os.Getenv("AWS_SSH_PUBLIC_KEY_FILE")
		}
	case "gce":
		// These environment variables are defined by the "preset-k8s-ssh" prow preset
		// https://github.com/kubernetes/test-infra/blob/432c6e7dca38f0785901a6159275524cec369c4a/config/prow/config.yaml#L639-L656
		if d.SSHPrivateKeyPath == "" {
			d.SSHPrivateKeyPath = os.Getenv("JENKINS_GCE_SSH_PRIVATE_KEY_FILE")
		}
		if d.SSHPublicKeyPath == "" {
			d.SSHPublicKeyPath = os.Getenv("JENKINS_GCE_SSH_PUBLIC_KEY_FILE")
		}
	}
	if d.SSHUser == "" {
		d.SSHUser = os.Getenv("KUBE_SSH_USER")
	}
	if d.TerraformVersion != "" {
		t, err := target.NewTerraform(d.TerraformVersion)
		if err != nil {
			return err
		}
		d.terraform = t
	}
	return nil
}

// verifyKopsFlags ensures common fields are set for kops commands
func (d *deployer) verifyKopsFlags() error {
	if d.ClusterName == "" {
		name, err := defaultClusterName(d.CloudProvider)
		if err != nil {
			return err
		}
		klog.Info("Using cluster name ", d.ClusterName)
		d.ClusterName = name
	}

	if d.KopsBinaryPath == "" && d.KopsVersionMarker == "" {
		if ws := os.Getenv("WORKSPACE"); ws != "" {
			d.KopsBinaryPath = path.Join(ws, "kops")
		} else {
			return errors.New("missing required --kops-binary-path when --kops-version-marker is not used")
		}
	}

	switch d.CloudProvider {
	case "aws":
	case "gce":
	case "digitalocean":
	default:
		return errors.New("unsupported --cloud-provider value")
	}

	if d.StateStore == "" {
		d.StateStore = stateStore(d.CloudProvider)
	}

	return nil
}

// env returns a list of environment variables passed to the kops binary
func (d *deployer) env() []string {
	vars := d.Env
	vars = append(vars, []string{
		fmt.Sprintf("PATH=%v", os.Getenv("PATH")),
		fmt.Sprintf("HOME=%v", os.Getenv("HOME")),
		fmt.Sprintf("KOPS_STATE_STORE=%v", d.StateStore),
		fmt.Sprintf("KOPS_FEATURE_FLAGS=%v", d.featureFlags()),
		"KOPS_RUN_TOO_NEW_VERSION=1",
	}...)
	if d.CloudProvider == "aws" {
		// Pass through some env vars if set
		for _, k := range []string{"AWS_PROFILE", "AWS_SHARED_CREDENTIALS_FILE"} {
			v := os.Getenv(k)
			if v != "" {
				vars = append(vars, k+"="+v)
			}
		}
	}
	if d.KopsBaseURL != "" {
		vars = append(vars, fmt.Sprintf("KOPS_BASE_URL=%v", d.KopsBaseURL))
	} else if baseURL := os.Getenv("KOPS_BASE_URL"); baseURL != "" {
		vars = append(vars, fmt.Sprintf("KOPS_BASE_URL=%v", os.Getenv("KOPS_BASE_URL")))
	}
	return vars
}

// featureFlags returns the kops feature flags to set
func (d *deployer) featureFlags() string {
	ff := []string{
		"+SpecOverrideFlag",
		"+AlphaAllowGCE",
	}
	val := strings.Join(ff, ",")
	for _, env := range d.Env {
		e := strings.Split(env, "=")
		if e[0] == "KOPS_FEATURE_FLAGS" && len(e) > 1 {
			val = fmt.Sprintf("%v,%v", val, e[1])
		}
	}
	return val
}

// defaultClusterName returns a kops cluster name to use when ClusterName is not set
func defaultClusterName(cloudProvider string) (string, error) {
	jobName := os.Getenv("JOB_NAME")
	buildID := os.Getenv("BUILD_ID")
	if jobName == "" || buildID == "" {
		return "", errors.New("JOB_NAME, and BUILD_ID env vars are required when --cluster-name is not set")
	}

	buildIDHash := fmt.Sprintf("%x", md5.Sum([]byte(buildID)))
	jobHash := fmt.Sprintf("%x", md5.Sum([]byte(jobName)))

	var suffix string
	switch cloudProvider {
	case "aws":
		suffix = "test-cncf-aws.k8s.io"
	default:
		suffix = "k8s.local"
	}

	return fmt.Sprintf("e2e-%v-%v.%v", buildIDHash[:10], jobHash[:5], suffix), nil
}

// stateStore returns the kops state store to use
// defaulting to values used in prow jobs
func stateStore(cloudProvider string) string {
	ss := os.Getenv("KOPS_STATE_STORE")
	if ss == "" {
		switch cloudProvider {
		case "aws":
			ss = "s3://k8s-kops-prow"
		case "gce":
			ss = "gs://k8s-kops-gce"
		}
	}
	return ss
}

// the default is $ARTIFACTS if set, otherwise ./_artifacts
// constructed as an absolute path to help the ginkgo tester because
// for some reason it needs an absolute path to the kubeconfig
func defaultArtifactsDir() (string, error) {
	if path, set := os.LookupEnv("ARTIFACTS"); set {
		absPath, err := filepath.Abs(path)
		if err != nil {
			return "", fmt.Errorf("failed to convert filepath from $ARTIFACTS (%s) to absolute path: %s", path, err)
		}
		return absPath, nil
	}

	absPath, err := filepath.Abs("_artifacts")
	if err != nil {
		return "", fmt.Errorf("when constructing default artifacts dir, failed to get absolute path: %s", err)
	}
	return absPath, nil
}
<|endoftext|>


<commit_before>package ui

import (
	`strings`
	`time`

	"github.com/nsf/termbox-go"
)

// Screen is thin wrapper aroung Termbox library to provide basic display
// capabilities as required by dry.
type Screen struct {
	Width    int     // Current number of columns.
	Height   int     // Current number of rows.
	cleared  bool    // True after the screens gets cleared.
	markup   *Markup // Pointer to markup processor (gets created by screen).
	pausedAt *time.Time
	Cursor   *Cursor // Pointer to cursor (gets created by screen).
}

//Cursor represents the cursor position on the screen
type Cursor struct {
	Line int
	Fg   termbox.Attribute
	Bg   termbox.Attribute
	Ch   rune
}

//NewScreen initializes Termbox, creates screen along with layout and markup, and
//calculates current screen dimensions. Once initialized the screen is
//ready for display.
func NewScreen() *Screen {

	if err := termbox.Init(); err != nil {
		panic(err)
	}
	termbox.SetOutputMode(termbox.Output256)
	screen := &Screen{}
	screen.markup = NewMarkup()
	screen.Cursor = &Cursor{Line: 0, Fg: termbox.ColorRed, Ch: '옷', Bg: termbox.Attribute(0x18)}

	return screen.Resize()
}

// Close gets called upon program termination to close the Termbox.
func (screen *Screen) Close() *Screen {
	termbox.Close()
	return screen
}

// Resize gets called when the screen is being resized. It recalculates screen
// dimensions and requests to clear the screen on next update.
func (screen *Screen) Resize() *Screen {
	screen.Width, screen.Height = termbox.Size()
	screen.cleared = false
	return screen
}

//Clear makes the entire screen blank using default background color.
func (screen *Screen) Clear() *Screen {
	termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
	screen.cleared = true
	termbox.Flush()
	return screen
}

// Sync forces a complete resync between the termbox and a terminal.
func (screen *Screen) Sync() *Screen {
	termbox.Sync()
	return screen
}

// ClearLine erases the contents of the line starting from (x,y) coordinate
// till the end of the line.
func (screen *Screen) ClearLine(x int, y int) *Screen {
	for i := x; i < screen.Width; i++ {
		termbox.SetCell(i, y, ' ', termbox.ColorDefault, termbox.ColorDefault)
	}
	termbox.Flush()

	return screen
}

//Flush synchronizes the internal buffer with the terminal.
func (screen *Screen) Flush() *Screen {
	termbox.Flush()
	return screen
}

// RenderLine takes the incoming string, tokenizes it to extract markup
// elements, and displays it all starting at (x,y) location.
func (screen *Screen) RenderLine(x int, y int, str string) {
	start, column := 0, 0

	for _, token := range Tokenize(str, screen.markup.supportedTags()) {
		// First check if it's a tag. Tags are eaten up and not displayed.
		if screen.markup.IsTag(token) {
			continue
		}

		// Here comes the actual text: displays it one character at a time.
		for i, char := range token {
			if !screen.markup.RightAligned {
				start = x + column
				column++
			} else {
				start = screen.Width - len(token) + i
			}
			termbox.SetCell(start, y, char, screen.markup.Foreground, screen.markup.Background)
		}
	}
}

//RenderLineWithBackGround does what RenderLine does but rendering the line
//with the given background color
func (screen *Screen) RenderLineWithBackGround(x int, y int, str string, bgColor uint16) {
	start, column := 0, 0
	for _, token := range Tokenize(str, screen.markup.supportedTags()) {
		// First check if it's a tag. Tags are eaten up and not displayed.
		if screen.markup.IsTag(token) {
			continue
		}

		// Here comes the actual text: display it one character at a time.
		for i, char := range token {
			if !screen.markup.RightAligned {
				start = x + column
				column++
			} else {
				start = screen.Width - len(token) + i
			}
			termbox.SetCell(start, y, char, screen.markup.Foreground, termbox.Attribute(bgColor))
		}
	}
	fill(start+1, y, screen.Width, 1, termbox.Cell{Ch: ' ', Bg: termbox.Attribute(bgColor)})
}

//ScrollCursorDown moves the cursor to the line below the current one
func (screen *Screen) ScrollCursorDown() {
	screen.Cursor.Line = screen.Cursor.Line + 1

}

//ScrollCursorUp moves the cursor to the line above the current one
func (screen *Screen) ScrollCursorUp() {
	screen.Cursor.Line = screen.Cursor.Line - 1
}

//CursorPosition tells on which screen line the cursor is
func (screen *Screen) CursorPosition() int {
	return screen.Cursor.Line
}

//Render renders the given content starting from
//the given row
func (screen *Screen) Render(initialRow int, str string) {
	if !screen.cleared {
		screen.Clear()
	}
	for row, line := range strings.Split(str, "\n") {
		screen.RenderLine(0, initialRow+row, line)
	}
}
<commit_msg>Add lock to Flush<commit_after>package ui

import (
	`strings`
	"sync"
	`time`

	"github.com/nsf/termbox-go"
)

// Screen is thin wrapper aroung Termbox library to provide basic display
// capabilities as required by dry.
type Screen struct {
	Width        int     // Current number of columns.
	Height       int     // Current number of rows.
	cleared      bool    // True after the screens gets cleared.
	markup       *Markup // Pointer to markup processor (gets created by screen).
	pausedAt     *time.Time
	Cursor       *Cursor // Pointer to cursor (gets created by screen).
	termboxMutex sync.Locker
}

//Cursor represents the cursor position on the screen
type Cursor struct {
	Line int
	Fg   termbox.Attribute
	Bg   termbox.Attribute
	Ch   rune
}

//NewScreen initializes Termbox, creates screen along with layout and markup, and
//calculates current screen dimensions. Once initialized the screen is
//ready for display.
func NewScreen() *Screen {

	if err := termbox.Init(); err != nil {
		panic(err)
	}
	termbox.SetOutputMode(termbox.Output256)
	screen := &Screen{}
	screen.markup = NewMarkup()
	screen.Cursor = &Cursor{Line: 0, Fg: termbox.ColorRed, Ch: '옷', Bg: termbox.Attribute(0x18)}
	screen.termboxMutex = &sync.Mutex{}
	return screen.Resize()
}

// Close gets called upon program termination to close the Termbox.
func (screen *Screen) Close() *Screen {
	termbox.Close()
	return screen
}

// Resize gets called when the screen is being resized. It recalculates screen
// dimensions and requests to clear the screen on next update.
func (screen *Screen) Resize() *Screen {
	screen.Width, screen.Height = termbox.Size()
	screen.cleared = false
	return screen
}

//Clear makes the entire screen blank using default background color.
func (screen *Screen) Clear() *Screen {
	termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
	screen.cleared = true
	screen.Flush()
	return screen
}

// Sync forces a complete resync between the termbox and a terminal.
func (screen *Screen) Sync() *Screen {
	termbox.Sync()
	return screen
}

// ClearLine erases the contents of the line starting from (x,y) coordinate
// till the end of the line.
func (screen *Screen) ClearLine(x int, y int) *Screen {
	for i := x; i < screen.Width; i++ {
		termbox.SetCell(i, y, ' ', termbox.ColorDefault, termbox.ColorDefault)
	}
	screen.Flush()

	return screen
}

//Flush synchronizes the internal buffer with the terminal.
func (screen *Screen) Flush() *Screen {
	screen.termboxMutex.Lock()
	defer screen.termboxMutex.Unlock()
	termbox.Flush()
	return screen
}

// RenderLine takes the incoming string, tokenizes it to extract markup
// elements, and displays it all starting at (x,y) location.
func (screen *Screen) RenderLine(x int, y int, str string) {
	start, column := 0, 0

	for _, token := range Tokenize(str, screen.markup.supportedTags()) {
		// First check if it's a tag. Tags are eaten up and not displayed.
		if screen.markup.IsTag(token) {
			continue
		}

		// Here comes the actual text: displays it one character at a time.
		for i, char := range token {
			if !screen.markup.RightAligned {
				start = x + column
				column++
			} else {
				start = screen.Width - len(token) + i
			}
			termbox.SetCell(start, y, char, screen.markup.Foreground, screen.markup.Background)
		}
	}
}

//RenderLineWithBackGround does what RenderLine does but rendering the line
//with the given background color
func (screen *Screen) RenderLineWithBackGround(x int, y int, str string, bgColor uint16) {
	start, column := 0, 0
	for _, token := range Tokenize(str, screen.markup.supportedTags()) {
		// First check if it's a tag. Tags are eaten up and not displayed.
		if screen.markup.IsTag(token) {
			continue
		}

		// Here comes the actual text: display it one character at a time.
		for i, char := range token {
			if !screen.markup.RightAligned {
				start = x + column
				column++
			} else {
				start = screen.Width - len(token) + i
			}
			termbox.SetCell(start, y, char, screen.markup.Foreground, termbox.Attribute(bgColor))
		}
	}
	fill(start+1, y, screen.Width, 1, termbox.Cell{Ch: ' ', Bg: termbox.Attribute(bgColor)})
}

//ScrollCursorDown moves the cursor to the line below the current one
func (screen *Screen) ScrollCursorDown() {
	screen.Cursor.Line = screen.Cursor.Line + 1

}

//ScrollCursorUp moves the cursor to the line above the current one
func (screen *Screen) ScrollCursorUp() {
	screen.Cursor.Line = screen.Cursor.Line - 1
}

//CursorPosition tells on which screen line the cursor is
func (screen *Screen) CursorPosition() int {
	return screen.Cursor.Line
}

//Render renders the given content starting from
//the given row
func (screen *Screen) Render(initialRow int, str string) {
	if !screen.cleared {
		screen.Clear()
	}
	for row, line := range strings.Split(str, "\n") {
		screen.RenderLine(0, initialRow+row, line)
	}
}
<|endoftext|>


<commit_before>// +build darwin
// +build cgo

package disk

/*
#cgo CFLAGS: -mmacosx-version-min=10.10 -DMACOSX_DEPLOYMENT_TARGET=10.10
#cgo LDFLAGS: -mmacosx-version-min=10.10 -lobjc -framework Foundation -framework IOKit
#include <stdint.h>

// ### enough?
const int MAX_DISK_NAME = 100;

typedef struct
{
    char DiskName[MAX_DISK_NAME];
    int64_t Reads;
    int64_t Writes;
    int64_t ReadBytes;
    int64_t WriteBytes;
    int64_t ReadTime;
    int64_t WriteTime;
} DiskInfo;

#include "disk_darwin.h"
*/
import "C"

import (
	"errors"
	"strings"
	"unsafe"

	"github.com/shirou/gopsutil/internal/common"
)

func IOCounters(names ...string) (map[string]IOCountersStat, error) {
	if C.StartIOCounterFetch() == 0 {
		return nil, errors.New("Unable to fetch disk list")
	}

	// Clean up when we are done.
	defer C.EndIOCounterFetch()
	ret := make(map[string]IOCountersStat, 0)

	for {
		res := C.FetchNextDisk()
		if res == -1 {
			return nil, errors.New("Unable to fetch disk information")
		} else if res == 0 {
			break // done
		}

		di := C.DiskInfo{}
		if C.ReadDiskInfo((*C.DiskInfo)(unsafe.Pointer(&di))) == -1 {
			return nil, errors.New("Unable to fetch disk properties")
		}

		// Used to only get the necessary part of the C string.
		isRuneNull := func(r rune) bool {
			return r == '\u0000'
		}

		// Map from the darwin-specific C struct to the Go type
		//
		// ### missing: IopsInProgress, WeightedIO, MergedReadCount,
		// MergedWriteCount, SerialNumber
		// IOKit can give us at least the serial number I think...
		d := IOCountersStat{
			// Note: The Go type wants unsigned values, but CFNumberGetValue
			// doesn't appear to be able to give us unsigned values. So, we
			// cast, and hope for the best.
			ReadBytes:  uint64(di.ReadBytes),
			WriteBytes: uint64(di.WriteBytes),
			ReadCount:  uint64(di.Reads),
			WriteCount: uint64(di.Writes),
			ReadTime:   uint64(di.ReadTime),
			WriteTime:  uint64(di.WriteTime),
			IoTime:     uint64(di.ReadTime + di.WriteTime),
			Name:       strings.TrimFunc(C.GoStringN(&di.DiskName[0], C.MAX_DISK_NAME), isRuneNull),
		}

		if len(names) > 0 && !common.StringsHas(names, d.Name) {
			continue
		}

		ret[d.Name] = d
	}

	return ret, nil
}
<commit_msg>disk: Remove -mmacosx-version-min from darwin+cgo<commit_after>// +build darwin
// +build cgo

package disk

/*
#cgo LDFLAGS: -lobjc -framework Foundation -framework IOKit
#include <stdint.h>

// ### enough?
const int MAX_DISK_NAME = 100;

typedef struct
{
    char DiskName[MAX_DISK_NAME];
    int64_t Reads;
    int64_t Writes;
    int64_t ReadBytes;
    int64_t WriteBytes;
    int64_t ReadTime;
    int64_t WriteTime;
} DiskInfo;

#include "disk_darwin.h"
*/
import "C"

import (
	"errors"
	"strings"
	"unsafe"

	"github.com/shirou/gopsutil/internal/common"
)

func IOCounters(names ...string) (map[string]IOCountersStat, error) {
	if C.StartIOCounterFetch() == 0 {
		return nil, errors.New("Unable to fetch disk list")
	}

	// Clean up when we are done.
	defer C.EndIOCounterFetch()
	ret := make(map[string]IOCountersStat, 0)

	for {
		res := C.FetchNextDisk()
		if res == -1 {
			return nil, errors.New("Unable to fetch disk information")
		} else if res == 0 {
			break // done
		}

		di := C.DiskInfo{}
		if C.ReadDiskInfo((*C.DiskInfo)(unsafe.Pointer(&di))) == -1 {
			return nil, errors.New("Unable to fetch disk properties")
		}

		// Used to only get the necessary part of the C string.
		isRuneNull := func(r rune) bool {
			return r == '\u0000'
		}

		// Map from the darwin-specific C struct to the Go type
		//
		// ### missing: IopsInProgress, WeightedIO, MergedReadCount,
		// MergedWriteCount, SerialNumber
		// IOKit can give us at least the serial number I think...
		d := IOCountersStat{
			// Note: The Go type wants unsigned values, but CFNumberGetValue
			// doesn't appear to be able to give us unsigned values. So, we
			// cast, and hope for the best.
			ReadBytes:  uint64(di.ReadBytes),
			WriteBytes: uint64(di.WriteBytes),
			ReadCount:  uint64(di.Reads),
			WriteCount: uint64(di.Writes),
			ReadTime:   uint64(di.ReadTime),
			WriteTime:  uint64(di.WriteTime),
			IoTime:     uint64(di.ReadTime + di.WriteTime),
			Name:       strings.TrimFunc(C.GoStringN(&di.DiskName[0], C.MAX_DISK_NAME), isRuneNull),
		}

		if len(names) > 0 && !common.StringsHas(names, d.Name) {
			continue
		}

		ret[d.Name] = d
	}

	return ret, nil
}
<|endoftext|>


<commit_before>package main

import (
	"html/template"
	"net/http"
)

// GLOBALS DECLARED HERE

var root = "/path/to/media"
var tmpl_dir = "./templates/"
var templates map[string]*template.Template
var pageData = PageData{}

// THE VIEW CODE IS HERE

func GenerateTemplates() {
	templates = make(map[string]*template.Template)
	modulus := template.FuncMap{"mod": func(i, j int) bool { return i%j == 0 }}
	templates_list := []string{"index.html", "about.html", "movie.html", "alreadyplaying.html"}
	for _, tmpl := range templates_list {
		t := template.New("base.html").Funcs(modulus)
		templates[tmpl] = template.Must(t.ParseFiles(tmpl_dir+"base.html", tmpl_dir+tmpl))

	}
}

func renderTemplate(w http.ResponseWriter, tmpl string) {
	err := templates[tmpl].Execute(w, pageData)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

// HANDLERS ARE HERE

func indexHandler(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	tmpl := "index.html"
	if r.Method == "GET" {
		if pageData.Player.Playing {
			tmpl = "alreadyplaying.html"
		}
	} else if r.Method == "POST" {
		if pageData.Player.Playing {
			player := &pageData.Player
			currentFilm := pageData.CurrentFilm
			pageData = PageData{}
			GenerateMovies()
			pageData.CurrentFilm = currentFilm
			pageData.Player = *player
		} else {
			pageData = PageData{}
			GenerateMovies()
		}
	}
	renderTemplate(w, tmpl)
}

func aboutHandler(w http.ResponseWriter, r *http.Request) {
	renderTemplate(w, "about.html")
}

func movieHandler(w http.ResponseWriter, r *http.Request) {
	command := r.URL.Query().Get("command")
	film := r.URL.Query().Get("movie")
	if pageData.Player.Playing == false {
		pageData.Player.StartFilm(film)
		pageData.CurrentFilm = film
	} else if pageData.Player.Playing && (film == "" || pageData.Player.FilmName == film) {
		if len(command) != 0 {
			if command == "kill" {
				pageData.Player.EndFilm()
				http.Redirect(w, r, "/", http.StatusFound)
				return
			} else {
				pageData.Player.SendCommandToFilm(command)
			}
		}
	} else {
		http.Redirect(w, r, "/", http.StatusFound)
		return
	}
	renderTemplate(w, "movie.html")
}

// IT ALL STARTS HERE

func main() {
	GenerateMovies()
	GenerateTemplates()
	http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
	http.HandleFunc("/", indexHandler)
	http.HandleFunc("/about", aboutHandler)
	http.HandleFunc("/movie", movieHandler)
	http.ListenAndServe(":8080", nil)
}
<commit_msg>Removed bug that occurs when refreshing the file list<commit_after>package main

import (
	"html/template"
	"net/http"
)

// GLOBALS DECLARED HERE

var root = "/path/to/media"
var tmpl_dir = "./templates/"
var templates map[string]*template.Template
var pageData = PageData{}

// THE VIEW CODE IS HERE

func GenerateTemplates() {
	templates = make(map[string]*template.Template)
	modulus := template.FuncMap{"mod": func(i, j int) bool { return i%j == 0 }}
	templates_list := []string{"index.html", "about.html", "movie.html", "alreadyplaying.html"}
	for _, tmpl := range templates_list {
		t := template.New("base.html").Funcs(modulus)
		templates[tmpl] = template.Must(t.ParseFiles(tmpl_dir+"base.html", tmpl_dir+tmpl))

	}
}

func renderTemplate(w http.ResponseWriter, tmpl string) {
	err := templates[tmpl].Execute(w, pageData)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

// HANDLERS ARE HERE

func indexHandler(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	tmpl := "index.html"
	if r.Method == "GET" {
		if pageData.Player.Playing {
			tmpl = "alreadyplaying.html"
		}
	} else if r.Method == "POST" {
		if pageData.Player.Playing {
			player := pageData.Player
			currentFilm := pageData.CurrentFilm
			pageData = PageData{}
			GenerateMovies()
			pageData.CurrentFilm = currentFilm
			pageData.Player = player
			tmpl = "alreadyplaying.html"
		} else {
			pageData = PageData{}
			GenerateMovies()
		}
	}
	renderTemplate(w, tmpl)
}

func aboutHandler(w http.ResponseWriter, r *http.Request) {
	renderTemplate(w, "about.html")
}

func movieHandler(w http.ResponseWriter, r *http.Request) {
	command := r.URL.Query().Get("command")
	film := r.URL.Query().Get("movie")
	if pageData.Player.Playing == false {
		pageData.Player.StartFilm(film)
		pageData.CurrentFilm = film
	} else if pageData.Player.Playing && (film == "" || pageData.Player.FilmName == film) {
		if len(command) != 0 {
			if command == "kill" {
				pageData.Player.EndFilm()
				http.Redirect(w, r, "/", http.StatusFound)
				return
			} else {
				pageData.Player.SendCommandToFilm(command)
			}
		}
	} else {
		http.Redirect(w, r, "/", http.StatusFound)
		return
	}
	renderTemplate(w, "movie.html")
}

// IT ALL STARTS HERE

func main() {
	GenerateMovies()
	GenerateTemplates()
	http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
	http.HandleFunc("/", indexHandler)
	http.HandleFunc("/about", aboutHandler)
	http.HandleFunc("/movie", movieHandler)
	http.ListenAndServe(":8080", nil)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/xml"
	"fmt"
	"log"
	"os"
	"strings"
	"time"

	"github.com/PuerkitoBio/goquery"
)

// basic struct to store information about a dish
type dish struct {
	category string
	name     string
}

// xml representation of a dish
type xmlDish struct {
	XMLName  xml.Name `xml:"dish"`
	Name     string   `xml:"name,attr"`
	Category string   `xml:"category,attr"`
}

// Wochentag contains the german names of the weekdays
var Wochentag = [7]string{"Sonntag", "Montag", "Dienstag", "Mittwoch",
	"Donnerstag", "Freitag", "Samstag"}

func main() {
	// Get the date for which we want to fetch the menu.
	// TODO try to handle holidays
	now := time.Now()
	// show mondays menu during the weekend
	if now.Weekday() == time.Saturday {
		now = now.Add(time.Duration(48) * time.Hour)
	} else if now.Weekday() == time.Sunday {
		now = now.Add(time.Duration(24) * time.Hour)
	} else {
		// show next date if it is later than 14:59
		if now.Hour() > 14 {
			// for friday the next date is monday
			if now.Weekday() == time.Friday {
				now = now.Add(time.Duration(72) * time.Hour)
			} else {
				// for all other cases it is simply the next day
				now = now.Add(time.Duration(24) * time.Hour)
			}
		}
	}
	// get string representation of the date
	var date string
	date = now.Format("2006-01-02")

	// TODO different locations
	location := "421"
	// create url
	baseURL := "http://www.studentenwerk-muenchen.de/mensa/speiseplan/"
	menuURL := baseURL + "speiseplan_" + date + "_" + location + "_-de.html"
	// fetch html from Studentenwerk
	doc, err := goquery.NewDocument(menuURL)
	if err != nil {
		log.Fatal(err)
	}
	// extract dishes
	dishes := make([]dish, 0, 6)
	var currentDish dish
	doc.Find(".c-schedule__list-item").Each(func(i int, s *goquery.Selection) {
		// get category (Tagesgericht, Aktionsessen, Beilage, ...)
		currentDish.category = s.Find(".stwm-artname").Text()
		// get dish description
		currentDish.name = s.Find(".js-schedule-dish-description").Text()
		// get rid of the "allergenkennzeichnungspflichtigen" ingredients
		currentDish.name = strings.Split(currentDish.name, "[")[0]
		// we are only interested in main dishes
		if strings.HasPrefix(currentDish.category, "Tagesgericht") ||
			strings.HasPrefix(currentDish.category, "Aktionsessen") ||
			strings.HasPrefix(currentDish.category, "Biogericht") ||
			currentDish.category == "Self-Service" {
			dishes = append(dishes, currentDish)
		}
	})

	outFile, err := os.Create("menu.xml")

	if err != nil {
		fmt.Println(err)
	}
	_, err = outFile.WriteString(xml.Header)
	if err != nil {
		log.Fatal(err)
	}

	var titleString string
	// if we failed to fetch any dishes, assume mensa is closed
	if len(dishes) == 0 {
		titleString = "Leider hat die Mensa am " + Wochentag[now.Weekday()] + " den " +
			now.Format("02.01.") + " geschlossen."
	} else {
		titleString = "Am " + Wochentag[now.Weekday()] + " den " +
			now.Format("02.01.") + " empfiehlt Küchenchef Horst Waldner:"
	}
	_, err = outFile.WriteString("<menu title=\"" + titleString + "\">\n")
	if err != nil {
		log.Fatal(err)
	}
	if len(dishes) > 0 {
		for i := range dishes {
			d := &xmlDish{Name: dishes[i].name, Category: dishes[i].category}
			output, err := xml.Marshal(d)
			if err != nil {
				log.Fatal(err)
			}
			outFile.Write(output)
			outFile.WriteString("\n")

		}
	}
	_, err = outFile.WriteString("</menu>\n")
	if err != nil {
		log.Fatal(err)
	}
	outFile.Close()
}
<commit_msg>create html for LIS-infoscreen<commit_after>package main

import (
	"fmt"
	"html"
	"log"
	"os"
	"strings"
	"time"

	"github.com/PuerkitoBio/goquery"
)

// struct to store information about a dish
type dish struct {
	category string
	name     string
}

// Wochentag contains the german names of the weekdays
var Wochentag = [7]string{"Sonntag", "Montag", "Dienstag", "Mittwoch",
	"Donnerstag", "Freitag", "Samstag"}

func main() {
	// Get the date for which we want to fetch the menu.
	// TODO try to handle holidays
	now := time.Now()
	// show mondays menu during the weekend
	if now.Weekday() == time.Saturday {
		now = now.Add(time.Duration(48) * time.Hour)
	} else if now.Weekday() == time.Sunday {
		now = now.Add(time.Duration(24) * time.Hour)
	} else {
		// show next date if it is later than 14:59
		if now.Hour() > 14 {
			// for friday the next date is monday
			if now.Weekday() == time.Friday {
				now = now.Add(time.Duration(72) * time.Hour)
			} else {
				// for all other cases it is simply the next day
				now = now.Add(time.Duration(24) * time.Hour)
			}
		}
	}
	// get string representation of the date
	var date string
	date = now.Format("2006-01-02")

	// TODO different locations
	location := "421"
	// create url
	baseURL := "http://www.studentenwerk-muenchen.de/mensa/speiseplan/"
	menuURL := baseURL + "speiseplan_" + date + "_" + location + "_-de.html"
	// fetch html from Studentenwerk
	doc, err := goquery.NewDocument(menuURL)
	if err != nil {
		log.Fatal(err)
	}
	// extract dishes
	dishes := make([]dish, 0, 6)
	var currentDish dish
	doc.Find(".c-schedule__list-item").Each(func(i int, s *goquery.Selection) {
		// get category (Tagesgericht, Aktionsessen, Beilage, ...)
		currentDish.category = s.Find(".stwm-artname").Text()
		// get dish description
		currentDish.name = s.Find(".js-schedule-dish-description").Text()
		// get rid of the "allergenkennzeichnungspflichtigen" ingredients
		currentDish.name = strings.Split(currentDish.name, "[")[0]
		// we are only interested in main dishes
		if strings.HasPrefix(currentDish.category, "Tagesgericht") ||
			strings.HasPrefix(currentDish.category, "Aktionsessen") ||
			strings.HasPrefix(currentDish.category, "Biogericht") ||
			currentDish.category == "Self-Service" {
			dishes = append(dishes, currentDish)
		}
	})

	outFile, err := os.Create("menu.html")
	if err != nil {
		fmt.Println(err)
	}

	titleString := "Mensa am " + Wochentag[now.Weekday()] + " den " +
		now.Format("02.01.")
	// set title
	outFile.WriteString("
" +
		html.EscapeString(titleString) + "
\n")
	// create box
	outFile.WriteString("
\n")
	// write dishes
	if len(dishes) > 0 {
		for i := range dishes {
			outFile.WriteString("
\n")
			outFile.WriteString("" +
				html.EscapeString(dishes[i].category) + "\n")
			outFile.WriteString("" +
				html.EscapeString(dishes[i].name) + "\n")
			outFile.WriteString("
\n")
		}
	} else {
		// if we failed to fetch any dishes, assume mensa is closed
		outFile.WriteString("Leider geschlossen.")
	}
	// close box
	outFile.WriteString("
\n")
	outFile.Close()
}
<|endoftext|>


<commit_before>// Copyright 2013, 2014 Peter Vasil, Tomo Krajina. All
// rights reserved. Use of this source code is governed
// by a BSD-style license that can be found in the
// LICENSE file.

package gpx

import (
	"encoding/xml"
	"fmt"
	"strings"
)

type Node struct {
	XMLName xml.Name
	Attrs   []xml.Attr `xml:",any,attr"`
	Data    string     `xml:",chardata"`
	Nodes   []Node     `xml:",any"`
}

func (n Node) toTokens(prefix string) (tokens []xml.Token) {
	fmt.Printf("name=%#v\n", n.XMLName)
	fmt.Printf("using prefix: %#v\n", prefix)
	var attrs []xml.Attr
	for _, a := range n.Attrs {
		fmt.Printf("attr=%#v\n", a)
		attrs = append(attrs, xml.Attr{Name: xml.Name{Local: a.Name.Local}, Value: a.Value})
	}

	start := xml.StartElement{Name: xml.Name{Local: n.XMLName.Local}, Attr: attrs}
	tokens = append(tokens, start)
	data := strings.TrimSpace(n.Data)
	if data != "" {
		tokens = append(tokens, xml.CharData(data))
	} else if len(n.Nodes) > 0 {
		for _, node := range n.Nodes {
			tokens = append(tokens, node.toTokens(prefix)...)
		}
	} else {
		return nil
	}
	tokens = append(tokens, xml.EndElement{start.Name})
	return
}

func (n Node) IsEmpty() bool     { return len(n.Nodes) == 0 && len(n.Attrs) == 0 && len(n.Data) == 0 }
func (n Node) LocalName() string { return n.XMLName.Local }
func (n Node) SpaceName() string { return n.XMLName.Space }

type Extension struct {
	Node

	// Filled before deserializing:
	namespaces map[string]string
}

var _ xml.Marshaler = Extension(Extension{})

func (ex Extension) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
	if len(ex.Node.Nodes) == 0 {
		return nil
	}

	fmt.Printf("start=%#v\n", start)

	start = xml.StartElement{Name: xml.Name{Local: start.Name.Local}, Attr: nil}
	tokens := []xml.Token{start}
	for _, node := range ex.Nodes {
		prefix := ""
		for k, v := range ex.namespaces {
			if v == node.SpaceName() {
				fmt.Println("prefix=", k)
				prefix = k
			}
		}
		tokens = append(tokens, node.toTokens(prefix)...)
	}

	tokens = append(tokens, xml.EndElement{Name: start.Name})

	for _, t := range tokens {
		err := e.EncodeToken(t)
		if err != nil {
			return err
		}
	}

	// flush to ensure tokens are written
	err := e.Flush()
	if err != nil {
		return err
	}

	return nil
}

/*

The GPX XML hierarchy:

gpx (gpxType)
    - attr: version (xsd:string) None
    - attr: creator (xsd:string) None
    metadata (metadataType)
        name (xsd:string)
        desc (xsd:string)
        author (personType)
            name (xsd:string)
            email (emailType)
                - attr: id (xsd:string) None
                - attr: domain (xsd:string) None
            link (linkType)
                - attr: href (xsd:anyURI) None
                text (xsd:string)
                type (xsd:string)
        copyright (copyrightType)
            - attr: author (xsd:string) None
            year (xsd:gYear)
            license (xsd:anyURI)
        link (linkType)
            - attr: href (xsd:anyURI) None
            text (xsd:string)
            type (xsd:string)
        time (xsd:dateTime)
        keywords (xsd:string)
        bounds (boundsType)
            - attr: minlat (latitudeType) None
            - attr: minlon (longitudeType) None
            - attr: maxlat (latitudeType) None
            - attr: maxlon (longitudeType) None
        extensions (extensionsType)
    wpt (wptType)
        - attr: lat (latitudeType) None
        - attr: lon (longitudeType) None
        ele (xsd:decimal)
        time (xsd:dateTime)
        magvar (degreesType)
        geoidheight (xsd:decimal)
        name (xsd:string)
        cmt (xsd:string)
        desc (xsd:string)
        src (xsd:string)
        link (linkType)
            - attr: href (xsd:anyURI) None
            text (xsd:string)
            type (xsd:string)
        sym (xsd:string)
        type (xsd:string)
        fix (fixType)
        sat (xsd:nonNegativeInteger)
        hdop (xsd:decimal)
        vdop (xsd:decimal)
        pdop (xsd:decimal)
        ageofdgpsdata (xsd:decimal)
        dgpsid (dgpsStationType)
        extensions (extensionsType)
    rte (rteType)
        name (xsd:string)
        cmt (xsd:string)
        desc (xsd:string)
        src (xsd:string)
        link (linkType)
            - attr: href (xsd:anyURI) None
            text (xsd:string)
            type (xsd:string)
        number (xsd:nonNegativeInteger)
        type (xsd:string)
        extensions (extensionsType)
        rtept (wptType)
            - attr: lat (latitudeType) None
            - attr: lon (longitudeType) None
            ele (xsd:decimal)
            time (xsd:dateTime)
            magvar (degreesType)
            geoidheight (xsd:decimal)
            name (xsd:string)
            cmt (xsd:string)
            desc (xsd:string)
            src (xsd:string)
            link (linkType)
                - attr: href (xsd:anyURI) None
                text (xsd:string)
                type (xsd:string)
            sym (xsd:string)
            type (xsd:string)
            fix (fixType)
            sat (xsd:nonNegativeInteger)
            hdop (xsd:decimal)
            vdop (xsd:decimal)
            pdop (xsd:decimal)
            ageofdgpsdata (xsd:decimal)
            dgpsid (dgpsStationType)
            extensions (extensionsType)
    trk (trkType)
        name (xsd:string)
        cmt (xsd:string)
        desc (xsd:string)
        src (xsd:string)
        link (linkType)
            - attr: href (xsd:anyURI) None
            text (xsd:string)
            type (xsd:string)
        number (xsd:nonNegativeInteger)
        type (xsd:string)
        extensions (extensionsType)
        trkseg (trksegType)
            trkpt (wptType)
                - attr: lat (latitudeType) None
                - attr: lon (longitudeType) None
                ele (xsd:decimal)
                time (xsd:dateTime)
                magvar (degreesType)
                geoidheight (xsd:decimal)
                name (xsd:string)
                cmt (xsd:string)
                desc (xsd:string)
                src (xsd:string)
                link (linkType)
                    - attr: href (xsd:anyURI) None
                    text (xsd:string)
                    type (xsd:string)
                sym (xsd:string)
                type (xsd:string)
                fix (fixType)
                sat (xsd:nonNegativeInteger)
                hdop (xsd:decimal)
                vdop (xsd:decimal)
                pdop (xsd:decimal)
                ageofdgpsdata (xsd:decimal)
                dgpsid (dgpsStationType)
                extensions (extensionsType)
            extensions (extensionsType)
    extensions (extensionsType)
*/

type gpx11Gpx struct {
	XMLName      xml.Name   `xml:"gpx"`
	Attrs        []xml.Attr `xml:",any,attr"`
	XMLNs        string     `xml:"xmlns,attr,omitempty"`
	XmlNsXsi     string     `xml:"xmlns:xsi,attr,omitempty"`
	XmlSchemaLoc string     `xml:"xsi:schemaLocation,attr,omitempty"`

	Version     string         `xml:"version,attr"`
	Creator     string         `xml:"creator,attr"`
	Name        string         `xml:"metadata>name,omitempty"`
	Desc        string         `xml:"metadata>desc,omitempty"`
	AuthorName  string         `xml:"metadata>author>name,omitempty"`
	AuthorEmail *gpx11GpxEmail `xml:"metadata>author>email,omitempty"`
	// TODO: There can be more than one link?
	AuthorLink *gpx11GpxLink      `xml:"metadata>author>link,omitempty"`
	Copyright  *gpx11GpxCopyright `xml:"metadata>copyright,omitempty"`
	Link       *gpx11GpxLink      `xml:"metadata>link,omitempty"`
	Timestamp  string             `xml:"metadata>time,omitempty"`
	Keywords   string             `xml:"metadata>keywords,omitempty"`
	Bounds     *gpx11GpxBounds    `xml:"bounds"`
	Extensions Extension          `xml:"extensions"`
	Waypoints  []*gpx11GpxPoint   `xml:"wpt"`
	Routes     []*gpx11GpxRte     `xml:"rte"`
	Tracks     []*gpx11GpxTrk     `xml:"trk"`
}

type gpx11GpxBounds struct {
	//XMLName xml.Name `xml:"bounds"`
	MinLat float64 `xml:"minlat,attr"`
	MaxLat float64 `xml:"maxlat,attr"`
	MinLon float64 `xml:"minlon,attr"`
	MaxLon float64 `xml:"maxlon,attr"`
}

type gpx11GpxCopyright struct {
	XMLName xml.Name `xml:"copyright"`
	Author  string   `xml:"author,attr"`
	Year    string   `xml:"year,omitempty"`
	License string   `xml:"license,omitempty"`
}

//type gpx11GpxAuthor struct {
//	Name  string        `xml:"name,omitempty"`
//	Email string        `xml:"email,omitempty"`
//	Link  *gpx11GpxLink `xml:"link"`
//}

type gpx11GpxEmail struct {
	Id     string `xml:"id,attr"`
	Domain string `xml:"domain,attr"`
}

type gpx11GpxLink struct {
	Href string `xml:"href,attr"`
	Text string `xml:"text,omitempty"`
	Type string `xml:"type,omitempty"`
}

//type gpx11GpxMetadata struct {
//	XMLName xml.Name        `xml:"metadata"`
//	Name    string          `xml:"name,omitempty"`
//	Desc    string          `xml:"desc,omitempty"`
//	Author  *gpx11GpxAuthor `xml:"author,omitempty"`
//	//	Copyright *GpxCopyright `xml:"copyright,omitempty"`
//	//	Links     []GpxLink     `xml:"link"`
//	Timestamp string `xml:"time,omitempty"`
//	Keywords  string `xml:"keywords,omitempty"`
//	//	Bounds    *GpxBounds    `xml:"bounds"`
//}

/**
 * Common struct fields for all points
 */
type gpx11GpxPoint struct {
	Lat float64 `xml:"lat,attr"`
	Lon float64 `xml:"lon,attr"`
	// Position info
	Ele         NullableFloat64 `xml:"ele,omitempty"`
	Timestamp   string          `xml:"time,omitempty"`
	MagVar      string          `xml:"magvar,omitempty"`
	GeoIdHeight string          `xml:"geoidheight,omitempty"`
	// Description info
	Name  string         `xml:"name,omitempty"`
	Cmt   string         `xml:"cmt,omitempty"`
	Desc  string         `xml:"desc,omitempty"`
	Src   string         `xml:"src,omitempty"`
	Links []gpx11GpxLink `xml:"link"`
	Sym   string         `xml:"sym,omitempty"`
	Type  string         `xml:"type,omitempty"`
	// Accuracy info
	Fix           string    `xml:"fix,omitempty"`
	Sat           *int      `xml:"sat,omitempty"`
	Hdop          *float64  `xml:"hdop,omitempty"`
	Vdop          *float64  `xml:"vdop,omitempty"`
	Pdop          *float64  `xml:"pdop,omitempty"`
	AgeOfDGpsData *float64  `xml:"ageofdgpsdata,omitempty"`
	DGpsId        *int      `xml:"dgpsid,omitempty"`
	Extensions    Extension `xml:"extensions"`
}

type gpx11GpxRte struct {
	XMLName xml.Name `xml:"rte"`
	Name    string   `xml:"name,omitempty"`
	Cmt     string   `xml:"cmt,omitempty"`
	Desc    string   `xml:"desc,omitempty"`
	Src     string   `xml:"src,omitempty"`
	// TODO
	//Links       []Link   `xml:"link"`
	Number NullableInt      `xml:"number,omitempty"`
	Type   string           `xml:"type,omitempty"`
	Points []*gpx11GpxPoint `xml:"rtept"`
}

type gpx11GpxTrkSeg struct {
	XMLName xml.Name         `xml:"trkseg"`
	Points  []*gpx11GpxPoint `xml:"trkpt"`
}

// Trk is a GPX track
type gpx11GpxTrk struct {
	XMLName xml.Name `xml:"trk"`
	Name    string   `xml:"name,omitempty"`
	Cmt     string   `xml:"cmt,omitempty"`
	Desc    string   `xml:"desc,omitempty"`
	Src     string   `xml:"src,omitempty"`
	// TODO
	//Links    []Link   `xml:"link"`
	Number   NullableInt       `xml:"number,omitempty"`
	Type     string            `xml:"type,omitempty"`
	Segments []*gpx11GpxTrkSeg `xml:"trkseg,omitempty"`
}
<commit_msg>But this does not set the prefix<commit_after>// Copyright 2013, 2014 Peter Vasil, Tomo Krajina. All
// rights reserved. Use of this source code is governed
// by a BSD-style license that can be found in the
// LICENSE file.

package gpx

import (
	"encoding/xml"
	"fmt"
	"strings"
)

type Node struct {
	XMLName xml.Name
	Attrs   []xml.Attr `xml:",any,attr"`
	Data    string     `xml:",chardata"`
	Nodes   []Node     `xml:",any"`
}

func (n Node) toTokens(prefix string) (tokens []xml.Token) {
	fmt.Printf("name=%#v\n", n.XMLName)
	fmt.Printf("using prefix: %#v\n", prefix)
	var attrs []xml.Attr
	for _, a := range n.Attrs {
		fmt.Printf("attr=%#v\n", a)
		attrs = append(attrs, xml.Attr{Name: xml.Name{Local: a.Name.Local}, Value: a.Value})
	}

	start := xml.StartElement{Name: xml.Name{Local: n.XMLName.Local, Space: prefix}, Attr: attrs}
	tokens = append(tokens, start)
	data := strings.TrimSpace(n.Data)
	if data != "" {
		tokens = append(tokens, xml.CharData(data))
	} else if len(n.Nodes) > 0 {
		for _, node := range n.Nodes {
			tokens = append(tokens, node.toTokens(prefix)...)
		}
	} else {
		return nil
	}
	tokens = append(tokens, xml.EndElement{start.Name})
	return
}

func (n Node) IsEmpty() bool     { return len(n.Nodes) == 0 && len(n.Attrs) == 0 && len(n.Data) == 0 }
func (n Node) LocalName() string { return n.XMLName.Local }
func (n Node) SpaceName() string { return n.XMLName.Space }

type Extension struct {
	Node

	// Filled before deserializing:
	namespaces map[string]string
}

var _ xml.Marshaler = Extension(Extension{})

func (ex Extension) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
	if len(ex.Node.Nodes) == 0 {
		return nil
	}

	fmt.Printf("start=%#v\n", start)

	start = xml.StartElement{Name: xml.Name{Local: start.Name.Local}, Attr: nil}
	tokens := []xml.Token{start}
	for _, node := range ex.Nodes {
		prefix := ""
		for k, v := range ex.namespaces {
			if v == node.SpaceName() {
				fmt.Println("prefix=", k)
				prefix = k
			}
		}
		tokens = append(tokens, node.toTokens(prefix)...)
	}

	tokens = append(tokens, xml.EndElement{Name: start.Name})

	for _, t := range tokens {
		err := e.EncodeToken(t)
		if err != nil {
			return err
		}
	}

	// flush to ensure tokens are written
	err := e.Flush()
	if err != nil {
		return err
	}

	return nil
}

/*

The GPX XML hierarchy:

gpx (gpxType)
    - attr: version (xsd:string) None
    - attr: creator (xsd:string) None
    metadata (metadataType)
        name (xsd:string)
        desc (xsd:string)
        author (personType)
            name (xsd:string)
            email (emailType)
                - attr: id (xsd:string) None
                - attr: domain (xsd:string) None
            link (linkType)
                - attr: href (xsd:anyURI) None
                text (xsd:string)
                type (xsd:string)
        copyright (copyrightType)
            - attr: author (xsd:string) None
            year (xsd:gYear)
            license (xsd:anyURI)
        link (linkType)
            - attr: href (xsd:anyURI) None
            text (xsd:string)
            type (xsd:string)
        time (xsd:dateTime)
        keywords (xsd:string)
        bounds (boundsType)
            - attr: minlat (latitudeType) None
            - attr: minlon (longitudeType) None
            - attr: maxlat (latitudeType) None
            - attr: maxlon (longitudeType) None
        extensions (extensionsType)
    wpt (wptType)
        - attr: lat (latitudeType) None
        - attr: lon (longitudeType) None
        ele (xsd:decimal)
        time (xsd:dateTime)
        magvar (degreesType)
        geoidheight (xsd:decimal)
        name (xsd:string)
        cmt (xsd:string)
        desc (xsd:string)
        src (xsd:string)
        link (linkType)
            - attr: href (xsd:anyURI) None
            text (xsd:string)
            type (xsd:string)
        sym (xsd:string)
        type (xsd:string)
        fix (fixType)
        sat (xsd:nonNegativeInteger)
        hdop (xsd:decimal)
        vdop (xsd:decimal)
        pdop (xsd:decimal)
        ageofdgpsdata (xsd:decimal)
        dgpsid (dgpsStationType)
        extensions (extensionsType)
    rte (rteType)
        name (xsd:string)
        cmt (xsd:string)
        desc (xsd:string)
        src (xsd:string)
        link (linkType)
            - attr: href (xsd:anyURI) None
            text (xsd:string)
            type (xsd:string)
        number (xsd:nonNegativeInteger)
        type (xsd:string)
        extensions (extensionsType)
        rtept (wptType)
            - attr: lat (latitudeType) None
            - attr: lon (longitudeType) None
            ele (xsd:decimal)
            time (xsd:dateTime)
            magvar (degreesType)
            geoidheight (xsd:decimal)
            name (xsd:string)
            cmt (xsd:string)
            desc (xsd:string)
            src (xsd:string)
            link (linkType)
                - attr: href (xsd:anyURI) None
                text (xsd:string)
                type (xsd:string)
            sym (xsd:string)
            type (xsd:string)
            fix (fixType)
            sat (xsd:nonNegativeInteger)
            hdop (xsd:decimal)
            vdop (xsd:decimal)
            pdop (xsd:decimal)
            ageofdgpsdata (xsd:decimal)
            dgpsid (dgpsStationType)
            extensions (extensionsType)
    trk (trkType)
        name (xsd:string)
        cmt (xsd:string)
        desc (xsd:string)
        src (xsd:string)
        link (linkType)
            - attr: href (xsd:anyURI) None
            text (xsd:string)
            type (xsd:string)
        number (xsd:nonNegativeInteger)
        type (xsd:string)
        extensions (extensionsType)
        trkseg (trksegType)
            trkpt (wptType)
                - attr: lat (latitudeType) None
                - attr: lon (longitudeType) None
                ele (xsd:decimal)
                time (xsd:dateTime)
                magvar (degreesType)
                geoidheight (xsd:decimal)
                name (xsd:string)
                cmt (xsd:string)
                desc (xsd:string)
                src (xsd:string)
                link (linkType)
                    - attr: href (xsd:anyURI) None
                    text (xsd:string)
                    type (xsd:string)
                sym (xsd:string)
                type (xsd:string)
                fix (fixType)
                sat (xsd:nonNegativeInteger)
                hdop (xsd:decimal)
                vdop (xsd:decimal)
                pdop (xsd:decimal)
                ageofdgpsdata (xsd:decimal)
                dgpsid (dgpsStationType)
                extensions (extensionsType)
            extensions (extensionsType)
    extensions (extensionsType)
*/

type gpx11Gpx struct {
	XMLName      xml.Name   `xml:"gpx"`
	Attrs        []xml.Attr `xml:",any,attr"`
	XMLNs        string     `xml:"xmlns,attr,omitempty"`
	XmlNsXsi     string     `xml:"xmlns:xsi,attr,omitempty"`
	XmlSchemaLoc string     `xml:"xsi:schemaLocation,attr,omitempty"`

	Version     string         `xml:"version,attr"`
	Creator     string         `xml:"creator,attr"`
	Name        string         `xml:"metadata>name,omitempty"`
	Desc        string         `xml:"metadata>desc,omitempty"`
	AuthorName  string         `xml:"metadata>author>name,omitempty"`
	AuthorEmail *gpx11GpxEmail `xml:"metadata>author>email,omitempty"`
	// TODO: There can be more than one link?
	AuthorLink *gpx11GpxLink      `xml:"metadata>author>link,omitempty"`
	Copyright  *gpx11GpxCopyright `xml:"metadata>copyright,omitempty"`
	Link       *gpx11GpxLink      `xml:"metadata>link,omitempty"`
	Timestamp  string             `xml:"metadata>time,omitempty"`
	Keywords   string             `xml:"metadata>keywords,omitempty"`
	Bounds     *gpx11GpxBounds    `xml:"bounds"`
	Extensions Extension          `xml:"extensions"`
	Waypoints  []*gpx11GpxPoint   `xml:"wpt"`
	Routes     []*gpx11GpxRte     `xml:"rte"`
	Tracks     []*gpx11GpxTrk     `xml:"trk"`
}

type gpx11GpxBounds struct {
	//XMLName xml.Name `xml:"bounds"`
	MinLat float64 `xml:"minlat,attr"`
	MaxLat float64 `xml:"maxlat,attr"`
	MinLon float64 `xml:"minlon,attr"`
	MaxLon float64 `xml:"maxlon,attr"`
}

type gpx11GpxCopyright struct {
	XMLName xml.Name `xml:"copyright"`
	Author  string   `xml:"author,attr"`
	Year    string   `xml:"year,omitempty"`
	License string   `xml:"license,omitempty"`
}

//type gpx11GpxAuthor struct {
//	Name  string        `xml:"name,omitempty"`
//	Email string        `xml:"email,omitempty"`
//	Link  *gpx11GpxLink `xml:"link"`
//}

type gpx11GpxEmail struct {
	Id     string `xml:"id,attr"`
	Domain string `xml:"domain,attr"`
}

type gpx11GpxLink struct {
	Href string `xml:"href,attr"`
	Text string `xml:"text,omitempty"`
	Type string `xml:"type,omitempty"`
}

//type gpx11GpxMetadata struct {
//	XMLName xml.Name        `xml:"metadata"`
//	Name    string          `xml:"name,omitempty"`
//	Desc    string          `xml:"desc,omitempty"`
//	Author  *gpx11GpxAuthor `xml:"author,omitempty"`
//	//	Copyright *GpxCopyright `xml:"copyright,omitempty"`
//	//	Links     []GpxLink     `xml:"link"`
//	Timestamp string `xml:"time,omitempty"`
//	Keywords  string `xml:"keywords,omitempty"`
//	//	Bounds    *GpxBounds    `xml:"bounds"`
//}

/**
 * Common struct fields for all points
 */
type gpx11GpxPoint struct {
	Lat float64 `xml:"lat,attr"`
	Lon float64 `xml:"lon,attr"`
	// Position info
	Ele         NullableFloat64 `xml:"ele,omitempty"`
	Timestamp   string          `xml:"time,omitempty"`
	MagVar      string          `xml:"magvar,omitempty"`
	GeoIdHeight string          `xml:"geoidheight,omitempty"`
	// Description info
	Name  string         `xml:"name,omitempty"`
	Cmt   string         `xml:"cmt,omitempty"`
	Desc  string         `xml:"desc,omitempty"`
	Src   string         `xml:"src,omitempty"`
	Links []gpx11GpxLink `xml:"link"`
	Sym   string         `xml:"sym,omitempty"`
	Type  string         `xml:"type,omitempty"`
	// Accuracy info
	Fix           string    `xml:"fix,omitempty"`
	Sat           *int      `xml:"sat,omitempty"`
	Hdop          *float64  `xml:"hdop,omitempty"`
	Vdop          *float64  `xml:"vdop,omitempty"`
	Pdop          *float64  `xml:"pdop,omitempty"`
	AgeOfDGpsData *float64  `xml:"ageofdgpsdata,omitempty"`
	DGpsId        *int      `xml:"dgpsid,omitempty"`
	Extensions    Extension `xml:"extensions"`
}

type gpx11GpxRte struct {
	XMLName xml.Name `xml:"rte"`
	Name    string   `xml:"name,omitempty"`
	Cmt     string   `xml:"cmt,omitempty"`
	Desc    string   `xml:"desc,omitempty"`
	Src     string   `xml:"src,omitempty"`
	// TODO
	//Links       []Link   `xml:"link"`
	Number NullableInt      `xml:"number,omitempty"`
	Type   string           `xml:"type,omitempty"`
	Points []*gpx11GpxPoint `xml:"rtept"`
}

type gpx11GpxTrkSeg struct {
	XMLName xml.Name         `xml:"trkseg"`
	Points  []*gpx11GpxPoint `xml:"trkpt"`
}

// Trk is a GPX track
type gpx11GpxTrk struct {
	XMLName xml.Name `xml:"trk"`
	Name    string   `xml:"name,omitempty"`
	Cmt     string   `xml:"cmt,omitempty"`
	Desc    string   `xml:"desc,omitempty"`
	Src     string   `xml:"src,omitempty"`
	// TODO
	//Links    []Link   `xml:"link"`
	Number   NullableInt       `xml:"number,omitempty"`
	Type     string            `xml:"type,omitempty"`
	Segments []*gpx11GpxTrkSeg `xml:"trkseg,omitempty"`
}
<|endoftext|>