text:
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file implements printing of AST nodes; specifically
// expressions, statements, declarations, and files. It uses
// the print functionality implemented in printer.go.

package printer

import (
	"bytes"
	"strconv"
	"strings"
	"unicode"
	"unicode/utf8"

	"github.com/tcard/sgo/sgo/ast"
	"github.com/tcard/sgo/sgo/token"
)

// Formatting issues:
// - better comment formatting for /*-style comments at the end of a line (e.g. a declaration)
//   when the comment spans multiple lines; if such a comment is just two lines, formatting is
//   not idempotent
// - formatting of expression lists
// - should use blank instead of tab to separate one-line function bodies from
//   the function header unless there is a group of consecutive one-liners

// ----------------------------------------------------------------------------
// Common AST nodes.

// Print as many newlines as necessary (but at least min newlines) to get to
// the current line. ws is printed before the first line break. If newSection
// is set, the first line break is printed as formfeed. Returns true if any
// line break was printed; returns false otherwise.
//
// TODO(gri): linebreak may add too many lines if the next statement at "line"
//            is preceded by comments because the computation of n assumes
//            the current position before the comment and the target position
//            after the comment. Thus, after interspersing such comments, the
//            space taken up by them is not considered to reduce the number of
//            linebreaks. At the moment there is no easy way to know about
//            future (not yet interspersed) comments in this function.
//
func (p *printer) linebreak(line, min int, ws whiteSpace, newSection bool) (printedBreak bool) {
	n := nlimit(line - p.pos.Line)
	if n < min {
		n = min
	}
	if n > 0 {
		p.print(ws)
		if newSection {
			p.print(formfeed)
			n--
		}
		for ; n > 0; n-- {
			p.print(newline)
		}
		printedBreak = true
	}
	return
}

// setComment sets g as the next comment if g != nil and if node comments
// are enabled - this mode is used when printing source code fragments such
// as exports only. It assumes that there is no pending comment in p.comments
// and at most one pending comment in the p.comment cache.
func (p *printer) setComment(g *ast.CommentGroup) {
	if g == nil || !p.useNodeComments {
		return
	}
	if p.comments == nil {
		// initialize p.comments lazily
		p.comments = make([]*ast.CommentGroup, 1)
	} else if p.cindex < len(p.comments) {
		// for some reason there are pending comments; this
		// should never happen - handle gracefully and flush
		// all comments up to g, ignore anything after that
		p.flush(p.posFor(g.List[0].Pos()), token.ILLEGAL)
		p.comments = p.comments[0:1]
		// in debug mode, report error
		p.internalError("setComment found pending comments")
	}
	p.comments[0] = g
	p.cindex = 0
	// don't overwrite any pending comment in the p.comment cache
	// (there may be a pending comment when a line comment is
	// immediately followed by a lead comment with no other
	// tokens between)
	if p.commentOffset == infinity {
		p.nextComment() // get comment ready for use
	}
}

type exprListMode uint

const (
	commaTerm exprListMode = 1 << iota // list is optionally terminated by a comma
	noIndent                           // no extra indentation in multi-line lists
)

// If indent is set, a multi-line identifier list is indented after the
// first linebreak encountered.
func (p *printer) identList(identList *ast.IdentList, indent bool) {
	var list []*ast.Ident
	if identList != nil {
		list = identList.List
		if identList.EntangledPos > 0 {
			beforeBacklash := list[:identList.EntangledPos-1]
			list = list[identList.EntangledPos-1:]
			if len(beforeBacklash) > 0 {
				p.idents(beforeBacklash, indent)
				p.print(blank)
			}
			p.print(token.BACKSL)
			if len(list) > 0 {
				p.print(blank)
			}
		}
	}
	p.idents(list, indent)
}

func (p *printer) idents(list []*ast.Ident, indent bool) {
	// convert into an expression list so we can re-use exprList formatting
	xlist := make([]ast.Expr, len(list))
	for i, x := range list {
		xlist[i] = x
	}
	var mode exprListMode
	if !indent {
		mode = noIndent
	}
	p.exprs(token.NoPos, xlist, 1, mode, token.NoPos)
}

// Print a list of expressions. If the list spans multiple
// source lines, the original line breaks are respected between
// expressions.
//
// TODO(gri) Consider rewriting this to be independent of []ast.Expr
//           so that we can use the algorithm for any kind of list
//           (e.g., pass list via a channel over which to range).
func (p *printer) exprList(prev0 token.Pos, exprList *ast.ExprList, depth int, mode exprListMode, next0 token.Pos) {
	var list []ast.Expr
	if exprList != nil {
		list = exprList.List
		if exprList.EntangledPos > 0 {
			beforeBacklash := list[:exprList.EntangledPos-1]
			list = list[exprList.EntangledPos-1:]
			if len(beforeBacklash) > 0 {
				p.exprs(prev0, beforeBacklash, depth, mode, next0)
				p.print(blank)
			}
			p.print(token.BACKSL)
			if len(list) > 0 {
				p.print(blank)
			}
		}
	}
	p.exprs(prev0, list, depth, mode, next0)
}

func (p *printer) exprs(prev0 token.Pos, list []ast.Expr, depth int, mode exprListMode, next0 token.Pos) {
	if len(list) == 0 {
		return
	}

	prev := p.posFor(prev0)
	next := p.posFor(next0)
	line := p.lineFor(list[0].Pos())
	endLine := p.lineFor(list[len(list)-1].End())

	if prev.IsValid() && prev.Line == line && line == endLine {
		// all list entries on a single line
		for i, x := range list {
			if i > 0 {
				// use position of expression following the comma as
				// comma position for correct comment placement
				p.print(x.Pos(), token.COMMA, blank)
			}
			p.expr0(x, depth)
		}
		return
	}

	// list entries span multiple lines;
	// use source code positions to guide line breaks

	// don't add extra indentation if noIndent is set;
	// i.e., pretend that the first line is already indented
	ws := ignore
	if mode&noIndent == 0 {
		ws = indent
	}

	// the first linebreak is always a formfeed since this section must not
	// depend on any previous formatting
	prevBreak := -1 // index of last expression that was followed by a linebreak
	if prev.IsValid() && prev.Line < line && p.linebreak(line, 0, ws, true) {
		ws = ignore
		prevBreak = 0
	}

	// initialize expression/key size: a zero value indicates expr/key doesn't fit on a single line
	size := 0

	// print all list elements
	prevLine := prev.Line
	for i, x := range list {
		line = p.lineFor(x.Pos())

		// determine if the next linebreak, if any, needs to use formfeed:
		// in general, use the entire node size to make the decision; for
		// key:value expressions, use the key size
		// TODO(gri) for a better result, should probably incorporate both
		//           the key and the node size into the decision process
		useFF := true

		// determine element size: all bets are off if we don't have
		// position information for the previous and next token (likely
		// generated code - simply ignore the size in this case by setting
		// it to 0)
		prevSize := size
		const infinity = 1e6 // larger than any source line
		size = p.nodeSize(x, infinity)
		pair, isPair := x.(*ast.KeyValueExpr)
		if size <= infinity && prev.IsValid() && next.IsValid() {
			// x fits on a single line
			if isPair {
				size = p.nodeSize(pair.Key, infinity) // size <= infinity
			}
		} else {
			// size too large or we don't have good layout information
			size = 0
		}

		// if the previous line and the current line had single-
		// line-expressions and the key sizes are small or the
		// the ratio between the key sizes does not exceed a
		// threshold, align columns and do not use formfeed
		if prevSize > 0 && size > 0 {
			const smallSize = 20
			if prevSize <= smallSize && size <= smallSize {
				useFF = false
			} else {
				const r = 4 // threshold
				ratio := float64(size) / float64(prevSize)
				useFF = ratio <= 1.0/r || r <= ratio
			}
		}

		needsLinebreak := 0 < prevLine && prevLine < line
		if i > 0 {
			// use position of expression following the comma as
			// comma position for correct comment placement, but
			// only if the expression is on the same line
			if !needsLinebreak {
				p.print(x.Pos())
			}
			p.print(token.COMMA)
			needsBlank := true
			if needsLinebreak {
				// lines are broken using newlines so comments remain aligned
				// unless forceFF is set or there are multiple expressions on
				// the same line in which case formfeed is used
				if p.linebreak(line, 0, ws, useFF || prevBreak+1 < i) {
					ws = ignore
					prevBreak = i
					needsBlank = false // we got a line break instead
				}
			}
			if needsBlank {
				p.print(blank)
			}
		}

		if len(list) > 1 && isPair && size > 0 && needsLinebreak {
			// we have a key:value expression that fits onto one line
			// and it's not on the same line as the prior expression:
			// use a column for the key such that consecutive entries
			// can align if possible
			// (needsLinebreak is set if we started a new line before)
			p.expr(pair.Key)
			p.print(pair.Colon, token.COLON, vtab)
			p.expr(pair.Value)
		} else {
			p.expr0(x, depth)
		}

		prevLine = line
	}

	if mode&commaTerm != 0 && next.IsValid() && p.pos.Line < next.Line {
		// print a terminating comma if the next token is on a new line
		p.print(token.COMMA)
		if ws == ignore && mode&noIndent == 0 {
			// unindent if we indented
			p.print(unindent)
		}
		p.print(formfeed) // terminating comma needs a line break to look good
		return
	}

	if ws == ignore && mode&noIndent == 0 {
		// unindent if we indented
		p.print(unindent)
	}
}

func (p *printer) parameters(fields *ast.FieldList) {
	p.print(fields.Opening, token.LPAREN)
	if len(fields.List) > 0 {
		prevLine := p.lineFor(fields.Opening)
		ws := indent
		for i, par := range append(fields.List, fields.Entangled) {
			if par == nil {
				continue
			}
			// determine par begin and end line (may be different
			// if there are multiple parameter names for this par
			// or the type is on a separate line)
			var parLineBeg int
			if len(par.Names) > 0 {
				parLineBeg = p.lineFor(par.Names[0].Pos())
			} else {
				parLineBeg = p.lineFor(par.Type.Pos())
			}
			var parLineEnd = p.lineFor(par.Type.End())
			// separating "," if needed
			needsLinebreak := 0 < prevLine && prevLine < parLineBeg
			if i > 0 {
				// use position of parameter following the comma as
				// comma position for correct comma placement, but
				// only if the next parameter is on the same line
				if !needsLinebreak {
					p.print(par.Pos())
				}
				if par == fields.Entangled {
					p.print(blank)
					p.print(token.BACKSL)
				} else {
					p.print(token.COMMA)
				}
			}
			// separator if needed (linebreak or blank)
			if needsLinebreak && p.linebreak(parLineBeg, 0, ws, true) {
				// break line if the opening "(" or previous parameter ended on a different line
				ws = ignore
			} else if i > 0 {
				p.print(blank)
			}
			// parameter names
			if len(par.Names) > 0 {
				// Very subtle: If we indented before (ws == ignore), identList
				// won't indent again. If we didn't (ws == indent), identList will
				// indent if the identList spans multiple lines, and it will outdent
				// again at the end (and still ws == indent). Thus, a subsequent indent
				// by a linebreak call after a type, or in the next multi-line identList
				// will do the right thing.
				p.idents(par.Names, ws == indent)
				p.print(blank)
			}
			// parameter type
			p.expr(stripParensAlways(par.Type))
			prevLine = parLineEnd
		}
		// if the closing ")" is on a separate line from the last parameter,
		// print an additional "," and line break
		if closing := p.lineFor(fields.Closing); 0 < prevLine && prevLine < closing {
			p.print(token.COMMA)
			p.linebreak(closing, 0, ignore, true)
		}
		// unindent if we indented
		if ws == ignore {
			p.print(unindent)
		}
	}
	p.print(fields.Closing, token.RPAREN)
}

func (p *printer) signature(params, result *ast.FieldList) {
	if params != nil {
		p.parameters(params)
	} else {
		p.print(token.LPAREN, token.RPAREN)
	}
	n := result.NumFields()
	if result != nil && result.Entangled != nil {
		n++
	}
	if n > 0 {
		// result != nil
		p.print(blank)
		if n == 1 && result.List[0].Names == nil {
			// single anonymous result; no ()'s
			p.expr(stripParensAlways(result.List[0].Type))
			return
		}
		p.parameters(result)
	}
}

func identListSize(list []*ast.Ident, maxSize int) (size int) {
	for i, x := range list {
		if i > 0 {
			size += len(", ")
		}
		size += utf8.RuneCountInString(x.Name)
		if size >= maxSize {
			break
		}
	}
	return
}

func (p *printer) isOneLineFieldList(list []*ast.Field) bool {
	if len(list) != 1 {
		return false // allow only one field
	}
	f := list[0]
	if f.Tag != nil || f.Comment != nil {
		return false // don't allow tags or comments
	}
	// only name(s) and type
	const maxSize = 30 // adjust as appropriate, this is an approximate value
	namesSize := identListSize(f.Names, maxSize)
	if namesSize > 0 {
		namesSize = 1 // blank between names and types
	}
	typeSize := p.nodeSize(f.Type, maxSize)
	return namesSize+typeSize <= maxSize
}

func (p *printer) setLineComment(text string) {
	p.setComment(&ast.CommentGroup{List: []*ast.Comment{{Slash: token.NoPos, Text: text}}})
}

func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool) {
	lbrace := fields.Opening
	list := fields.List
	rbrace := fields.Closing
	hasComments := isIncomplete || p.commentBefore(p.posFor(rbrace))
	srcIsOneLine := lbrace.IsValid() && rbrace.IsValid() && p.lineFor(lbrace) == p.lineFor(rbrace)

	if !hasComments && srcIsOneLine {
		// possibly a one-line struct/interface
		if len(list) == 0 {
			// no blank between keyword and {} in this case
			p.print(lbrace, token.LBRACE, rbrace, token.RBRACE)
			return
		} else if p.isOneLineFieldList(list) {
			// small enough - print on one line
			// (don't use identList and ignore source line breaks)
			p.print(lbrace, token.LBRACE, blank)
			f := list[0]
			if isStruct {
				for i, x := range f.Names {
					if i > 0 {
						// no comments so no need for comma position
						p.print(token.COMMA, blank)
					}
					p.expr(x)
				}
				if len(f.Names) > 0 {
					p.print(blank)
				}
				p.expr(f.Type)
			} else { // interface
				if ftyp, isFtyp := f.Type.(*ast.FuncType); isFtyp {
					// method
					p.expr(f.Names[0])
					p.signature(ftyp.Params, ftyp.Results)
				} else {
					// embedded interface
					p.expr(f.Type)
				}
			}
			p.print(blank, rbrace, token.RBRACE)
			return
		}
	}
	// hasComments || !srcIsOneLine

	p.print(blank, lbrace, token.LBRACE, indent)
	if hasComments || len(list) > 0 {
		p.print(formfeed)
	}

	if isStruct {

		sep := vtab
		if len(list) == 1 {
			sep = blank
		}
		var line int
		for i, f := range list {
			if i > 0 {
				p.linebreak(p.lineFor(f.Pos()), 1, ignore, p.linesFrom(line) > 0)
			}
			extraTabs := 0
			p.setComment(f.Doc)
			p.recordLine(&line)
			if len(f.Names) > 0 {
				// named fields
				p.idents(f.Names, false)
				p.print(sep)
				p.expr(f.Type)
				extraTabs = 1
			} else {
				// anonymous field
				p.expr(f.Type)
				extraTabs = 2
			}
			if f.Tag != nil {
				if len(f.Names) > 0 && sep == vtab {
					p.print(sep)
				}
				p.print(sep)
				p.expr(f.Tag)
				extraTabs = 0
			}
			if f.Comment != nil {
				for ; extraTabs > 0; extraTabs-- {
					p.print(sep)
				}
				p.setComment(f.Comment)
			}
		}
		if isIncomplete {
			if len(list) > 0 {
				p.print(formfeed)
			}
			p.flush(p.posFor(rbrace), token.RBRACE) // make sure we don't lose the last line comment
			p.setLineComment("// contains filtered or unexported fields")
		}

	} else { // interface

		var line int
		for i, f := range list {
			if i > 0 {
				p.linebreak(p.lineFor(f.Pos()), 1, ignore, p.linesFrom(line) > 0)
			}
			p.setComment(f.Doc)
			p.recordLine(&line)
			if ftyp, isFtyp := f.Type.(*ast.FuncType); isFtyp {
				// method
				p.expr(f.Names[0])
				p.signature(ftyp.Params, ftyp.Results)
			} else {
				// embedded interface
				p.expr(f.Type)
			}
			p.setComment(f.Comment)
		}
		if isIncomplete {
			if len(list) > 0 {
				p.print(formfeed)
			}
			p.flush(p.posFor(rbrace), token.RBRACE) // make sure we don't lose the last line comment
			p.setLineComment("// contains filtered or unexported methods")
		}

	}
	p.print(unindent, formfeed, rbrace, token.RBRACE)
}

// ----------------------------------------------------------------------------
// Expressions

func walkBinary(e *ast.BinaryExpr) (has4, has5 bool, maxProblem int) {
	switch e.Op.Precedence() {
	case 4:
		has4 = true
	case 5:
		has5 = true
	}

	switch l := e.X.(type) {
	case *ast.BinaryExpr:
		if l.Op.Precedence() < e.Op.Precedence() {
			// parens will be inserted.
			// pretend this is an *ast.ParenExpr and do nothing.
			break
		}
		h4, h5, mp := walkBinary(l)
		has4 = has4 || h4
		has5 = has5 || h5
		if maxProblem < mp {
			maxProblem = mp
		}
	}

	switch r := e.Y.(type) {
	case *ast.BinaryExpr:
		if r.Op.Precedence() <= e.Op.Precedence() {
			// parens will be inserted.
			// pretend this is an *ast.ParenExpr and do nothing.
			break
		}
		h4, h5, mp := walkBinary(r)
		has4 = has4 || h4
		has5 = has5 || h5
		if maxProblem < mp {
			maxProblem = mp
		}

	case *ast.StarExpr:
		if e.Op == token.QUO { // `*/`
			maxProblem = 5
		}

	case *ast.UnaryExpr:
		switch e.Op.String() + r.Op.String() {
		case "/*", "&&", "&^":
			maxProblem = 5
		case "++", "--":
			if maxProblem < 4 {
				maxProblem = 4
			}
		}
	}
	return
}

func cutoff(e *ast.BinaryExpr, depth int) int {
	has4, has5, maxProblem := walkBinary(e)
	if maxProblem > 0 {
		return maxProblem + 1
	}
	if has4 && has5 {
		if depth == 1 {
			return 5
		}
		return 4
	}
	if depth == 1 {
		return 6
	}
	return 4
}

func diffPrec(expr ast.Expr, prec int) int {
	x, ok := expr.(*ast.BinaryExpr)
	if !ok || prec != x.Op.Precedence() {
		return 1
	}
	return 0
}

func reduceDepth(depth int) int {
	depth--
	if depth < 1 {
		depth = 1
	}
	return depth
}

// Format the binary expression: decide the cutoff and then format.
// Let's call depth == 1 Normal mode, and depth > 1 Compact mode.
// (Algorithm suggestion by Russ Cox.)
//
// The precedences are:
//	5             *  /  %  <<  >>  &  &^
//	4             +  -  |  ^
//	3             ==  !=  <  <=  >  >=
//	2             &&
//	1             ||
//
// The only decision is whether there will be spaces around levels 4 and 5.
// There are never spaces at level 6 (unary), and always spaces at levels 3 and below.
//
// To choose the cutoff, look at the whole expression but excluding primary
// expressions (function calls, parenthesized exprs), and apply these rules:
//
//	1) If there is a binary operator with a right side unary operand
//	   that would clash without a space, the cutoff must be (in order):
//
//		/*	6
//		&&	6
//		&^	6
//		++	5
//		--	5
//
//         (Comparison operators always have spaces around them.)
//
//	2) If there is a mix of level 5 and level 4 operators, then the cutoff
//	   is 5 (use spaces to distinguish precedence) in Normal mode
//	   and 4 (never use spaces) in Compact mode.
//
//	3) If there are no level 4 operators or no level 5 operators, then the
//	   cutoff is 6 (always use spaces) in Normal mode
//	   and 4 (never use spaces) in Compact mode.
//
func (p *printer) binaryExpr(x *ast.BinaryExpr, prec1, cutoff, depth int) {
	prec := x.Op.Precedence()
	if prec < prec1 {
		// parenthesis needed
		// Note: The parser inserts an ast.ParenExpr node; thus this case
		//       can only occur if the AST is created in a different way.
		p.print(token.LPAREN)
		p.expr0(x, reduceDepth(depth)) // parentheses undo one level of depth
		p.print(token.RPAREN)
		return
	}

	printBlank := prec < cutoff

	ws := indent
	p.expr1(x.X, prec, depth+diffPrec(x.X, prec))
	if printBlank {
		p.print(blank)
	}
	xline := p.pos.Line // before the operator (it may be on the next line!)
	yline := p.lineFor(x.Y.Pos())
	p.print(x.OpPos, x.Op)
	if xline != yline && xline > 0 && yline > 0 {
		// at least one line break, but respect an extra empty line
		// in the source
		if p.linebreak(yline, 1, ws, true) {
			ws = ignore
			printBlank = false // no blank after line break
		}
	}
	if printBlank {
		p.print(blank)
	}
	p.expr1(x.Y, prec+1, depth+1)
	if ws == ignore {
		p.print(unindent)
	}
}

func isBinary(expr ast.Expr) bool {
	_, ok := expr.(*ast.BinaryExpr)
	return ok
}

func (p *printer) expr1(expr ast.Expr, prec1, depth int) {
	p.print(expr.Pos())

	switch x := expr.(type) {
	case *ast.BadExpr:
		p.print("BadExpr")

	case *ast.Ident:
		p.print(x)

	case *ast.BinaryExpr:
		if depth < 1 {
			p.internalError("depth < 1:", depth)
			depth = 1
		}
		p.binaryExpr(x, prec1, cutoff(x, depth), depth)

	case *ast.KeyValueExpr:
		p.expr(x.Key)
		p.print(x.Colon, token.COLON, blank)
		p.expr(x.Value)

	case *ast.StarExpr:
		const prec = token.UnaryPrec
		if prec < prec1 {
			// parenthesis needed
			p.print(token.LPAREN)
			p.print(token.MUL)
			p.expr(x.X)
			p.print(token.RPAREN)
		} else {
			// no parenthesis needed
			p.print(token.MUL)
			p.expr(x.X)
		}

	case *ast.UnaryExpr:
		const prec = token.UnaryPrec
		if prec < prec1 {
			// parenthesis needed
			p.print(token.LPAREN)
			p.expr(x)
			p.print(token.RPAREN)
		} else {
			// no parenthesis needed
			p.print(x.Op)
			if x.Op == token.RANGE {
				// TODO(gri) Remove this code if it cannot be reached.
				p.print(blank)
			}
			p.expr1(x.X, prec, depth)
		}

	case *ast.BasicLit:
		p.print(x)

	case *ast.FuncLit:
		p.expr(x.Type)
		p.funcBody(p.distanceFrom(x.Type.Pos()), blank, x.Body)

	case *ast.ParenExpr:
		if _, hasParens := x.X.(*ast.ParenExpr); hasParens {
			// don't print parentheses around an already parenthesized expression
			// TODO(gri) consider making this more general and incorporate precedence levels
			p.expr0(x.X, depth)
		} else {
			p.print(token.LPAREN)
			p.expr0(x.X, reduceDepth(depth)) // parentheses undo one level of depth
			p.print(x.Rparen, token.RPAREN)
		}

	case *ast.SelectorExpr:
		p.selectorExpr(x, depth, false)

	case *ast.ForceExpr:
		p.expr1(x.X, token.HighestPrec, depth)
		p.print(x.Mark, token.NOT)

	case *ast.TypeAssertExpr:
		p.expr1(x.X, token.HighestPrec, depth)
		p.print(token.PERIOD, x.Lparen, token.LPAREN)
		if x.Type != nil {
			p.expr(x.Type)
		} else {
			p.print(token.TYPE)
		}
		p.print(x.Rparen, token.RPAREN)

	case *ast.IndexExpr:
		// TODO(gri): should treat[] like parentheses and undo one level of depth
		p.expr1(x.X, token.HighestPrec, 1)
		p.print(x.Lbrack, token.LBRACK)
		p.expr0(x.Index, depth+1)
		p.print(x.Rbrack, token.RBRACK)

	case *ast.SliceExpr:
		// TODO(gri): should treat[] like parentheses and undo one level of depth
		p.expr1(x.X, token.HighestPrec, 1)
		p.print(x.Lbrack, token.LBRACK)
		indices := []ast.Expr{x.Low, x.High}
		if x.Max != nil {
			indices = append(indices, x.Max)
		}
		// determine if we need extra blanks around ':'
		var needsBlanks bool
		if depth <= 1 {
			var indexCount int
			var hasBinaries bool
			for _, x := range indices {
				if x != nil {
					indexCount++
					if isBinary(x) {
						hasBinaries = true
					}
				}
			}
			if indexCount > 1 && hasBinaries {
				needsBlanks = true
			}
		}
		for i, x := range indices {
			if i > 0 {
				if indices[i-1] != nil && needsBlanks {
					p.print(blank)
				}
				p.print(token.COLON)
				if x != nil && needsBlanks {
					p.print(blank)
				}
			}
			if x != nil {
				p.expr0(x, depth+1)
			}
		}
		p.print(x.Rbrack, token.RBRACK)

	case *ast.CallExpr:
		if len(x.Args) > 1 {
			depth++
		}
		var wasIndented bool
		if _, ok := x.Fun.(*ast.FuncType); ok {
			// conversions to literal function types require parentheses around the type
			p.print(token.LPAREN)
			wasIndented = p.possibleSelectorExpr(x.Fun, token.HighestPrec, depth)
			p.print(token.RPAREN)
		} else {
			wasIndented = p.possibleSelectorExpr(x.Fun, token.HighestPrec, depth)
		}
		p.print(x.Lparen, token.LPAREN)
		if x.Ellipsis.IsValid() {
			p.exprs(x.Lparen, x.Args, depth, 0, x.Ellipsis)
			p.print(x.Ellipsis, token.ELLIPSIS)
			if x.Rparen.IsValid() && p.lineFor(x.Ellipsis) < p.lineFor(x.Rparen) {
				p.print(token.COMMA, formfeed)
			}
		} else {
			p.exprs(x.Lparen, x.Args, depth, commaTerm, x.Rparen)
		}
		p.print(x.Rparen, token.RPAREN)
		if wasIndented {
			p.print(unindent)
		}

	case *ast.CompositeLit:
		// composite literal elements that are composite literals themselves may have the type omitted
		if x.Type != nil {
			p.expr1(x.Type, token.HighestPrec, depth)
		}
		p.level++
		p.print(x.Lbrace, token.LBRACE)
		p.exprs(x.Lbrace, x.Elts, 1, commaTerm, x.Rbrace)
		// do not insert extra line break following a /*-style comment
		// before the closing '}' as it might break the code if there
		// is no trailing ','
		mode := noExtraLinebreak
		// do not insert extra blank following a /*-style comment
		// before the closing '}' unless the literal is empty
		if len(x.Elts) > 0 {
			mode |= noExtraBlank
		}
		// need the initial indent to print lone comments with
		// the proper level of indentation
		p.print(indent, unindent, mode, x.Rbrace, token.RBRACE, mode)
		p.level--

	case *ast.Ellipsis:
		p.print(token.ELLIPSIS)
		if x.Elt != nil {
			p.expr(x.Elt)
		}

	case *ast.ArrayType:
		p.print(token.LBRACK)
		if x.Len != nil {
			p.expr(x.Len)
		}
		p.print(token.RBRACK)
		p.expr(x.Elt)

	case *ast.StructType:
		p.print(token.STRUCT)
		p.fieldList(x.Fields, true, x.Incomplete)

	case *ast.FuncType:
		p.print(token.FUNC)
		p.signature(x.Params, x.Results)

	case *ast.InterfaceType:
		p.print(token.INTERFACE)
		p.fieldList(x.Methods, false, x.Incomplete)

	case *ast.MapType:
		p.print(token.MAP, token.LBRACK)
		p.expr(x.Key)
		p.print(token.RBRACK)
		p.expr(x.Value)

	case *ast.ChanType:
		switch x.Dir {
		case ast.SEND | ast.RECV:
			p.print(token.CHAN)
		case ast.RECV:
			p.print(token.ARROW, token.CHAN) // x.Arrow and x.Pos() are the same
		case ast.SEND:
			p.print(token.CHAN, x.Arrow, token.ARROW)
		}
		p.print(blank)
		p.expr(x.Value)

	case *ast.OptionalType:
		p.print(token.QUEST)
		p.expr(x.Elt)

	default:
		panic("unreachable")
	}
}

func (p *printer) possibleSelectorExpr(expr ast.Expr, prec1, depth int) bool {
	if x, ok := expr.(*ast.SelectorExpr); ok {
		return p.selectorExpr(x, depth, true)
	}
	p.expr1(expr, prec1, depth)
	return false
}

// selectorExpr handles an *ast.SelectorExpr node and returns whether x spans
// multiple lines.
func (p *printer) selectorExpr(x *ast.SelectorExpr, depth int, isMethod bool) bool {
	p.expr1(x.X, token.HighestPrec, depth)
	p.print(token.PERIOD)
	if line := p.lineFor(x.Sel.Pos()); p.pos.IsValid() && p.pos.Line < line {
		p.print(indent, newline, x.Sel.Pos(), x.Sel)
		if !isMethod {
			p.print(unindent)
		}
		return true
	}
	p.print(x.Sel.Pos(), x.Sel)
	return false
}

func (p *printer) expr0(x ast.Expr, depth int) {
	p.expr1(x, token.LowestPrec, depth)
}

func (p *printer) expr(x ast.Expr) {
	const depth = 1
	p.expr1(x, token.LowestPrec, depth)
}

// ----------------------------------------------------------------------------
// Statements

// Print the statement list indented, but without a newline after the last statement.
// Extra line breaks between statements in the source are respected but at most one
// empty line is printed between statements.
func (p *printer) stmtList(list []ast.Stmt, nindent int, nextIsRBrace bool) {
	if nindent > 0 {
		p.print(indent)
	}
	var line int
	i := 0
	for _, s := range list {
		// ignore empty statements (was issue 3466)
		if _, isEmpty := s.(*ast.EmptyStmt); !isEmpty {
			// nindent == 0 only for lists of switch/select case clauses;
			// in those cases each clause is a new section
			if len(p.output) > 0 {
				// only print line break if we are not at the beginning of the output
				// (i.e., we are not printing only a partial program)
				p.linebreak(p.lineFor(s.Pos()), 1, ignore, i == 0 || nindent == 0 || p.linesFrom(line) > 0)
			}
			p.recordLine(&line)
			p.stmt(s, nextIsRBrace && i == len(list)-1)
			// labeled statements put labels on a separate line, but here
			// we only care about the start line of the actual statement
			// without label - correct line for each label
			for t := s; ; {
				lt, _ := t.(*ast.LabeledStmt)
				if lt == nil {
					break
				}
				line++
				t = lt.Stmt
			}
			i++
		}
	}
	if nindent > 0 {
		p.print(unindent)
	}
}

// block prints an *ast.BlockStmt; it always spans at least two lines.
func (p *printer) block(b *ast.BlockStmt, nindent int) {
	p.print(b.Lbrace, token.LBRACE)
	p.stmtList(b.List, nindent, true)
	p.linebreak(p.lineFor(b.Rbrace), 1, ignore, true)
	p.print(b.Rbrace, token.RBRACE)
}

func isTypeName(x ast.Expr) bool {
	switch t := x.(type) {
	case *ast.Ident:
		return true
	case *ast.SelectorExpr:
		return isTypeName(t.X)
	}
	return false
}

func stripParens(x ast.Expr) ast.Expr {
	if px, strip := x.(*ast.ParenExpr); strip {
		// parentheses must not be stripped if there are any
		// unparenthesized composite literals starting with
		// a type name
		ast.Inspect(px.X, func(node ast.Node) bool {
			switch x := node.(type) {
			case *ast.ParenExpr:
				// parentheses protect enclosed composite literals
				return false
			case *ast.CompositeLit:
				if isTypeName(x.Type) {
					strip = false // do not strip parentheses
				}
				return false
			}
			// in all other cases, keep inspecting
			return true
		})
		if strip {
			return stripParens(px.X)
		}
	}
	return x
}

func stripParensAlways(x ast.Expr) ast.Expr {
	if x, ok := x.(*ast.ParenExpr); ok {
		return stripParensAlways(x.X)
	}
	return x
}

func (p *printer) controlClause(isForStmt bool, init ast.Stmt, expr ast.Expr, post ast.Stmt) {
	p.print(blank)
	needsBlank := false
	if init == nil && post == nil {
		// no semicolons required
		if expr != nil {
			p.expr(stripParens(expr))
			needsBlank = true
		}
	} else {
		// all semicolons required
		// (they are not separators, print them explicitly)
		if init != nil {
			p.stmt(init, false)
		}
		p.print(token.SEMICOLON, blank)
		if expr != nil {
			p.expr(stripParens(expr))
			needsBlank = true
		}
		if isForStmt {
			p.print(token.SEMICOLON, blank)
			needsBlank = false
			if post != nil {
				p.stmt(post, false)
				needsBlank = true
			}
		}
	}
	if needsBlank {
		p.print(blank)
	}
}

// indentList reports whether an expression list would look better if it
// were indented wholesale (starting with the very first element, rather
// than starting at the first line break).
//
func (p *printer) indentList(exprList *ast.ExprList) bool {
	var list []ast.Expr
	if exprList != nil {
		list = exprList.List
	}
	return p.indentExprs(list)
}

func (p *printer) indentExprs(list []ast.Expr) bool {
	// Heuristic: indentList returns true if there are more than one multi-
	// line element in the list, or if there is any element that is not
	// starting on the same line as the previous one ends.
	if len(list) >= 2 {
		var b = p.lineFor(list[0].Pos())
		var e = p.lineFor(list[len(list)-1].End())
		if 0 < b && b < e {
			// list spans multiple lines
			n := 0 // multi-line element count
			line := b
			for _, x := range list {
				xb := p.lineFor(x.Pos())
				xe := p.lineFor(x.End())
				if line < xb {
					// x is not starting on the same
					// line as the previous one ended
					return true
				}
				if xb < xe {
					// x is a multi-line element
					n++
				}
				line = xe
			}
			return n > 1
		}
	}
	return false
}

func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool) {
	p.print(stmt.Pos())

	switch s := stmt.(type) {
	case *ast.BadStmt:
		p.print("BadStmt")

	case *ast.DeclStmt:
		p.decl(s.Decl)

	case *ast.EmptyStmt:
		// nothing to do

	case *ast.LabeledStmt:
		// a "correcting" unindent immediately following a line break
		// is applied before the line break if there is no comment
		// between (see writeWhitespace)
		p.print(unindent)
		p.expr(s.Label)
		p.print(s.Colon, token.COLON, indent)
		if e, isEmpty := s.Stmt.(*ast.EmptyStmt); isEmpty {
			if !nextIsRBrace {
				p.print(newline, e.Pos(), token.SEMICOLON)
				break
			}
		} else {
			p.linebreak(p.lineFor(s.Stmt.Pos()), 1, ignore, true)
		}
		p.stmt(s.Stmt, nextIsRBrace)

	case *ast.ExprStmt:
		const depth = 1
		p.expr0(s.X, depth)

	case *ast.SendStmt:
		const depth = 1
		p.expr0(s.Chan, depth)
		p.print(blank, s.Arrow, token.ARROW, blank)
		p.expr0(s.Value, depth)

	case *ast.IncDecStmt:
		const depth = 1
		p.expr0(s.X, depth+1)
		p.print(s.TokPos, s.Tok)

	case *ast.AssignStmt:
		var depth = 1
		if s.Lhs.Len() > 1 && s.Rhs.Len() > 1 {
			depth++
		}
		p.exprList(s.Pos(), s.Lhs, depth, 0, s.TokPos)
		p.print(blank, s.TokPos, s.Tok, blank)
		p.exprList(s.TokPos, s.Rhs, depth, 0, token.NoPos)

	case *ast.GoStmt:
		p.print(token.GO, blank)
		p.expr(s.Call)

	case *ast.DeferStmt:
		p.print(token.DEFER, blank)
		p.expr(s.Call)

	case *ast.ReturnStmt:
		p.print(token.RETURN)
		if s.Results != nil {
			p.print(blank)
			// Use indentList heuristic to make corner cases look
			// better (issue 1207). A more systematic approach would
			// always indent, but this would cause significant
			// reformatting of the code base and not necessarily
			// lead to more nicely formatted code in general.
			if p.indentList(s.Results) {
				p.print(indent)
				p.exprList(s.Pos(), s.Results, 1, noIndent, token.NoPos)
				p.print(unindent)
			} else {
				p.exprList(s.Pos(), s.Results, 1, 0, token.NoPos)
			}
		}

	case *ast.BranchStmt:
		p.print(s.Tok)
		if s.Label != nil {
			p.print(blank)
			p.expr(s.Label)
		}

	case *ast.BlockStmt:
		p.block(s, 1)

	case *ast.IfStmt:
		p.print(token.IF)
		p.controlClause(false, s.Init, s.Cond, nil)
		p.block(s.Body, 1)
		if s.Else != nil {
			p.print(blank, token.ELSE, blank)
			switch s.Else.(type) {
			case *ast.BlockStmt, *ast.IfStmt:
				p.stmt(s.Else, nextIsRBrace)
			default:
				// This can only happen with an incorrectly
				// constructed AST. Permit it but print so
				// that it can be parsed without errors.
				p.print(token.LBRACE, indent, formfeed)
				p.stmt(s.Else, true)
				p.print(unindent, formfeed, token.RBRACE)
			}
		}

	case *ast.CaseClause:
		if s.List.Len() != 0 {
			p.print(token.CASE, blank)
			p.exprList(s.Pos(), s.List, 1, 0, s.Colon)
		} else {
			p.print(token.DEFAULT)
		}
		p.print(s.Colon, token.COLON)
		p.stmtList(s.Body, 1, nextIsRBrace)

	case *ast.SwitchStmt:
		p.print(token.SWITCH)
		p.controlClause(false, s.Init, s.Tag, nil)
		p.block(s.Body, 0)

	case *ast.TypeSwitchStmt:
		p.print(token.SWITCH)
		if s.Init != nil {
			p.print(blank)
			p.stmt(s.Init, false)
			p.print(token.SEMICOLON)
		}
		p.print(blank)
		p.stmt(s.Assign, false)
		p.print(blank)
		p.block(s.Body, 0)

	case *ast.CommClause:
		if s.Comm != nil {
			p.print(token.CASE, blank)
			p.stmt(s.Comm, false)
		} else {
			p.print(token.DEFAULT)
		}
		p.print(s.Colon, token.COLON)
		p.stmtList(s.Body, 1, nextIsRBrace)

	case *ast.SelectStmt:
		p.print(token.SELECT, blank)
		body := s.Body
		if len(body.List) == 0 && !p.commentBefore(p.posFor(body.Rbrace)) {
			// print empty select statement w/o comments on one line
			p.print(body.Lbrace, token.LBRACE, body.Rbrace, token.RBRACE)
		} else {
			p.block(body, 0)
		}

	case *ast.ForStmt:
		p.print(token.FOR)
		p.controlClause(true, s.Init, s.Cond, s.Post)
		p.block(s.Body, 1)

	case *ast.RangeStmt:
		p.print(token.FOR, blank)
		if s.Key != nil {
			p.expr(s.Key)
			if s.Value != nil {
				// use position of value following the comma as
				// comma position for correct comment placement
				p.print(s.Value.Pos(), token.COMMA, blank)
				p.expr(s.Value)
			}
			p.print(blank, s.TokPos, s.Tok, blank)
		}
		p.print(token.RANGE, blank)
		p.expr(stripParens(s.X))
		p.print(blank)
		p.block(s.Body, 1)

	default:
		panic("unreachable")
	}
}

// ----------------------------------------------------------------------------
// Declarations

// The keepTypeColumn function determines if the type column of a series of
// consecutive const or var declarations must be kept, or if initialization
// values (V) can be placed in the type column (T) instead. The i'th entry
// in the result slice is true if the type column in spec[i] must be kept.
//
// For example, the declaration:
//
//	const (
//		foobar int = 42 // comment
//		x          = 7  // comment
//		foo
//              bar = 991
//	)
//
// leads to the type/values matrix below. A run of value columns (V) can
// be moved into the type column if there is no type for any of the values
// in that column (we only move entire columns so that they align properly).
//
//	matrix        formatted     result
//                    matrix
//	T  V    ->    T  V     ->   true      there is a T and so the type
//	-  V          -  V          true      column must be kept
//	-  -          -  -          false
//	-  V          V  -          false     V is moved into T column
//
func keepTypeColumn(specs []ast.Spec) []bool {
	m := make([]bool, len(specs))

	populate := func(i, j int, keepType bool) {
		if keepType {
			for ; i < j; i++ {
				m[i] = true
			}
		}
	}

	i0 := -1 // if i0 >= 0 we are in a run and i0 is the start of the run
	var keepType bool
	for i, s := range specs {
		t := s.(*ast.ValueSpec)
		if t.Values.Len() != 0 {
			if i0 < 0 {
				// start of a run of ValueSpecs with non-nil Values
				i0 = i
				keepType = false
			}
		} else {
			if i0 >= 0 {
				// end of a run
				populate(i0, i, keepType)
				i0 = -1
			}
		}
		if t.Type != nil {
			keepType = true
		}
	}
	if i0 >= 0 {
		// end of a run
		populate(i0, len(specs), keepType)
	}

	return m
}

func (p *printer) valueSpec(s *ast.ValueSpec, keepType bool) {
	p.setComment(s.Doc)
	p.identList(s.Names, false) // always present
	extraTabs := 3
	if s.Type != nil || keepType {
		p.print(vtab)
		extraTabs--
	}
	if s.Type != nil {
		p.expr(s.Type)
	}
	if s.Values.Len() > 0 {
		p.print(vtab, token.ASSIGN, blank)
		p.exprList(token.NoPos, s.Values, 1, 0, token.NoPos)
		extraTabs--
	}
	if s.Comment != nil {
		for ; extraTabs > 0; extraTabs-- {
			p.print(vtab)
		}
		p.setComment(s.Comment)
	}
}

func sanitizeImportPath(lit *ast.BasicLit) *ast.BasicLit {
	// Note: An unmodified AST generated by go/parser will already
	// contain a backward- or double-quoted path string that does
	// not contain any invalid characters, and most of the work
	// here is not needed. However, a modified or generated AST
	// may possibly contain non-canonical paths. Do the work in
	// all cases since it's not too hard and not speed-critical.

	// if we don't have a proper string, be conservative and return whatever we have
	if lit.Kind != token.STRING {
		return lit
	}
	s, err := strconv.Unquote(lit.Value)
	if err != nil {
		return lit
	}

	// if the string is an invalid path, return whatever we have
	//
	// spec: "Implementation restriction: A compiler may restrict
	// ImportPaths to non-empty strings using only characters belonging
	// to Unicode's L, M, N, P, and S general categories (the Graphic
	// characters without spaces) and may also exclude the characters
	// !"#$%&'()*,:;<=>?[\]^`{|} and the Unicode replacement character
	// U+FFFD."
	if s == "" {
		return lit
	}
	const illegalChars = `!"#$%&'()*,:;<=>?[\]^{|}` + "`\uFFFD"
	for _, r := range s {
		if !unicode.IsGraphic(r) || unicode.IsSpace(r) || strings.ContainsRune(illegalChars, r) {
			return lit
		}
	}

	// otherwise, return the double-quoted path
	s = strconv.Quote(s)
	if s == lit.Value {
		return lit // nothing wrong with lit
	}
	return &ast.BasicLit{ValuePos: lit.ValuePos, Kind: token.STRING, Value: s}
}

// The parameter n is the number of specs in the group. If doIndent is set,
// multi-line identifier lists in the spec are indented when the first
// linebreak is encountered.
//
func (p *printer) spec(spec ast.Spec, n int, doIndent bool) {
	switch s := spec.(type) {
	case *ast.ImportSpec:
		p.setComment(s.Doc)
		if s.Name != nil {
			p.expr(s.Name)
			p.print(blank)
		}
		p.expr(sanitizeImportPath(s.Path))
		p.setComment(s.Comment)
		p.print(s.EndPos)

	case *ast.ValueSpec:
		if n != 1 {
			p.internalError("expected n = 1; got", n)
		}
		p.setComment(s.Doc)
		p.identList(s.Names, doIndent) // always present
		if s.Type != nil {
			p.print(blank)
			p.expr(s.Type)
		}
		if s.Values.Len() != 0 {
			p.print(blank, token.ASSIGN, blank)
			p.exprList(token.NoPos, s.Values, 1, 0, token.NoPos)
		}
		p.setComment(s.Comment)

	case *ast.TypeSpec:
		p.setComment(s.Doc)
		p.expr(s.Name)
		if n == 1 {
			p.print(blank)
		} else {
			p.print(vtab)
		}
		if s.Assign.IsValid() {
			p.print(token.ASSIGN, blank)
		}
		p.expr(s.Type)
		p.setComment(s.Comment)

	default:
		panic("unreachable")
	}
}

func (p *printer) genDecl(d *ast.GenDecl) {
	p.setComment(d.Doc)
	p.print(d.Pos(), d.Tok, blank)

	if d.Lparen.IsValid() {
		// group of parenthesized declarations
		p.print(d.Lparen, token.LPAREN)
		if n := len(d.Specs); n > 0 {
			p.print(indent, formfeed)
			if n > 1 && (d.Tok == token.CONST || d.Tok == token.VAR) {
				// two or more grouped const/var declarations:
				// determine if the type column must be kept
				keepType := keepTypeColumn(d.Specs)
				var line int
				for i, s := range d.Specs {
					if i > 0 {
						p.linebreak(p.lineFor(s.Pos()), 1, ignore, p.linesFrom(line) > 0)
					}
					p.recordLine(&line)
					p.valueSpec(s.(*ast.ValueSpec), keepType[i])
				}
			} else {
				var line int
				for i, s := range d.Specs {
					if i > 0 {
						p.linebreak(p.lineFor(s.Pos()), 1, ignore, p.linesFrom(line) > 0)
					}
					p.recordLine(&line)
					p.spec(s, n, false)
				}
			}
			p.print(unindent, formfeed)
		}
		p.print(d.Rparen, token.RPAREN)

	} else {
		// single declaration
		p.spec(d.Specs[0], 1, true)
	}
}

// nodeSize determines the size of n in chars after formatting.
// The result is <= maxSize if the node fits on one line with at
// most maxSize chars and the formatted output doesn't contain
// any control chars. Otherwise, the result is > maxSize.
//
func (p *printer) nodeSize(n ast.Node, maxSize int) (size int) {
	// nodeSize invokes the printer, which may invoke nodeSize
	// recursively. For deep composite literal nests, this can
	// lead to an exponential algorithm. Remember previous
	// results to prune the recursion (was issue 1628).
	if size, found := p.nodeSizes[n]; found {
		return size
	}

	size = maxSize + 1 // assume n doesn't fit
	p.nodeSizes[n] = size

	// nodeSize computation must be independent of particular
	// style so that we always get the same decision; print
	// in RawFormat
	cfg := Config{Mode: RawFormat}
	var buf bytes.Buffer
	if err := cfg.fprint(&buf, p.fset, n, p.nodeSizes); err != nil {
		return
	}
	if buf.Len() <= maxSize {
		for _, ch := range buf.Bytes() {
			if ch < ' ' {
				return
			}
		}
		size = buf.Len() // n fits
		p.nodeSizes[n] = size
	}
	return
}

// numLines returns the number of lines spanned by node n in the original source.
func (p *printer) numLines(n ast.Node) int {
	if from := n.Pos(); from.IsValid() {
		if to := n.End(); to.IsValid() {
			return p.lineFor(to) - p.lineFor(from) + 1
		}
	}
	return infinity
}

// bodySize is like nodeSize but it is specialized for *ast.BlockStmt's.
func (p *printer) bodySize(b *ast.BlockStmt, maxSize int) int {
	pos1 := b.Pos()
	pos2 := b.Rbrace
	if pos1.IsValid() && pos2.IsValid() && p.lineFor(pos1) != p.lineFor(pos2) {
		// opening and closing brace are on different lines - don't make it a one-liner
		return maxSize + 1
	}
	if len(b.List) > 5 {
		// too many statements - don't make it a one-liner
		return maxSize + 1
	}
	// otherwise, estimate body size
	bodySize := p.commentSizeBefore(p.posFor(pos2))
	for i, s := range b.List {
		if bodySize > maxSize {
			break // no need to continue
		}
		if i > 0 {
			bodySize += 2 // space for a semicolon and blank
		}
		bodySize += p.nodeSize(s, maxSize)
	}
	return bodySize
}

// funcBody prints a function body following a function header of given headerSize.
// If the header's and block's size are "small enough" and the block is "simple enough",
// the block is printed on the current line, without line breaks, spaced from the header
// by sep. Otherwise the block's opening "{" is printed on the current line, followed by
// lines for the block's statements and its closing "}".
//
func (p *printer) funcBody(headerSize int, sep whiteSpace, b *ast.BlockStmt) {
	if b == nil {
		return
	}

	// save/restore composite literal nesting level
	defer func(level int) {
		p.level = level
	}(p.level)
	p.level = 0

	const maxSize = 100
	if headerSize+p.bodySize(b, maxSize) <= maxSize {
		p.print(sep, b.Lbrace, token.LBRACE)
		if len(b.List) > 0 {
			p.print(blank)
			for i, s := range b.List {
				if i > 0 {
					p.print(token.SEMICOLON, blank)
				}
				p.stmt(s, i == len(b.List)-1)
			}
			p.print(blank)
		}
		p.print(noExtraLinebreak, b.Rbrace, token.RBRACE, noExtraLinebreak)
		return
	}

	if sep != ignore {
		p.print(blank) // always use blank
	}
	p.block(b, 1)
}

// distanceFrom returns the column difference between from and p.pos (the current
// estimated position) if both are on the same line; if they are on different lines
// (or unknown) the result is infinity.
func (p *printer) distanceFrom(from token.Pos) int {
	if from.IsValid() && p.pos.IsValid() {
		if f := p.posFor(from); f.Line == p.pos.Line {
			return p.pos.Column - f.Column
		}
	}
	return infinity
}

func (p *printer) funcDecl(d *ast.FuncDecl) {
	p.setComment(d.Doc)
	p.print(d.Pos(), token.FUNC, blank)
	if d.Recv != nil {
		p.parameters(d.Recv) // method: print receiver
		p.print(blank)
	}
	p.expr(d.Name)
	p.signature(d.Type.Params, d.Type.Results)
	p.funcBody(p.distanceFrom(d.Pos()), vtab, d.Body)
}

func (p *printer) decl(decl ast.Decl) {
	switch d := decl.(type) {
	case *ast.BadDecl:
		p.print(d.Pos(), "BadDecl")
	case *ast.GenDecl:
		p.genDecl(d)
	case *ast.FuncDecl:
		p.funcDecl(d)
	default:
		panic("unreachable")
	}
}

// ----------------------------------------------------------------------------
// Files

func declToken(decl ast.Decl) (tok token.Token) {
	tok = token.ILLEGAL
	switch d := decl.(type) {
	case *ast.GenDecl:
		tok = d.Tok
	case *ast.FuncDecl:
		tok = token.FUNC
	}
	return
}

func (p *printer) declList(list []ast.Decl) {
	tok := token.ILLEGAL
	for _, d := range list {
		prev := tok
		tok = declToken(d)
		// If the declaration token changed (e.g., from CONST to TYPE)
		// or the next declaration has documentation associated with it,
		// print an empty line between top-level declarations.
		// (because p.linebreak is called with the position of d, which
		// is past any documentation, the minimum requirement is satisfied
		// even w/o the extra getDoc(d) nil-check - leave it in case the
		// linebreak logic improves - there's already a TODO).
		if len(p.output) > 0 {
			// only print line break if we are not at the beginning of the output
			// (i.e., we are not printing only a partial program)
			min := 1
			if prev != tok || getDoc(d) != nil {
				min = 2
			}
			// start a new section if the next declaration is a function
			// that spans multiple lines (see also issue #19544)
			p.linebreak(p.lineFor(d.Pos()), min, ignore, tok == token.FUNC && p.numLines(d) > 1)
		}
		p.decl(d)
	}
}

func (p *printer) file(src *ast.File) {
	p.setComment(src.Doc)
	p.print(src.Pos(), token.PACKAGE, blank)
	p.expr(src.Name)
	p.declList(src.Decls)
	p.print(newline)
}


text:
This app demonstrates the use of drawing text with CoreText.

This app also employs three gesture recognizers:

- Pan gesture (to move the view);

- Pinch gesture (to resize the view); and

- Rotation gesture (to rotate the view).

--

If you have any questions, do not hesitate to post an issue on GitHub and let me know.

Rob Ryan

17 April 2013

redpajama_set_name: RedPajamaGithub


text:
module Striker
	class Tag < Site

		attr_reader :tag

		def initialize(tag=nil, options={})
			@tag = tag
			@settings = self.settings
			@options = options
		end

		def pages
			pages = []
			Dir.chdir(@settings.pages_dir)
			Dir.glob("*[.md|.markdown]").each do |page|
				page = Page.new(page, { :site_meta => @options[:site_meta] })
				pages << page.page_data if page.meta['tagged'] and page.meta['tagged'].include? tag
			end
			pages
		end

		def list
			tags = []
			Dir.chdir(@settings.pages_dir)
			Dir.glob("*[.md|.markdown]").each do |file|
				page = Page.new(file, { :site_meta => @options[:site_meta] })
				tags << page.meta['tagged'] if page.meta['tagged']
			end
			tags.size > 0 ? tags.flatten.uniq : tags
		end

		def list_full
			tags = []
			Dir.chdir(self.settings.pages_dir)
			pages = Dir.glob("*[.md|.markdown]").map{ |page| Page.new(page) }
			if self.list
				self.list.each do |tag|
					tagged = []
					pages.each do |page|
						tagged << page.page_data if page.meta['tagged'] and page.meta['tagged'].include? tag
					end
					tags << { 'name' => tag, 'url' => File.join("/", @settings.baseurl, @settings.config['tagged']['permalink'], @settings.config['tagged']['name'], tag), 'pages' => tagged }
				end
			end
			tags
		end

		def process
			self.list.each do |tag|
				FileUtils.mkdir_p(File.join(@settings.basepath, @settings.config['tagged']['permalink'], @settings.config['tagged']['name'], tag))
			end
			process_tags
		end

		private
		def process_tags
			template = File.open(File.join(@settings.templates_dir, "tags/tag.html"), "r").read
			self.list.each do |tag|
				tagged = { 'tag' => tag, 'pages' => Tag.new(tag).pages }
				File.open(File.join(@settings.basepath, @settings.config['tagged']['permalink'], @settings.config['tagged']['name'], tag, "index.html"), "w") do |f|
					f.write Liquid::Template.parse(template).render(
						'site' => @options[:site_meta],
						'tagged' => tagged
					)
				end
			end
		end

	end
end


text:
require "hbc/cli/abstract_command"

module Hbc
  class CLI
    module Compat
      class Update < AbstractCommand
        def run
          odisabled "`brew cask update`", "`brew update`"
        end

        def self.visible
          false
        end
      end
    end

    prepend Compat
  end
end


text:
module Tripletexer::Endpoints
  class Country < AbstractEndpoint

    # https://tripletex.no/v2-docs/#!/country/search
    def search(params = {})
      find_entities('/v2/country', params)
    end

    # https://tripletex.no/v2-docs/#!/country/get
    def find(id, params = {})
      find_entity("/v2/country/#{id}", params)
    end

  end
end


text:
namespace DomainNamesFilter.Core.MatchAlgorithms
{
    using System.Collections.Generic;

    using DomainNamesFilter.Core.DTO;

    /// <summary>
    /// Интерфейс алгоритма фильтрации доменов
    /// </summary>
    public interface IDomainsMatchAlgorithm
    {
        #region Public Methods and Operators

        /// <summary>
        /// Формирует коллекцию доменов не входящих в черный список
        /// </summary>
        /// <param name="sourceDomains">
        /// Исходная коллекция доменов
        /// </param>
        /// <param name="blackListDomains">
        /// Черный список доменов
        /// </param>
        /// <returns>
        /// Отфильтрованная коллекция доменов
        /// </returns>
        IEnumerable<Domain> Match(IEnumerable<Domain> sourceDomains, IEnumerable<Domain> blackListDomains);

        #endregion
    }
}


text:
layout: post
title: React Native - Learn once, write anywhere, Build mobile apps with React
category: 'technology'
---

##  Build native mobile apps using JavaScript and React

React Native lets you build mobile apps using only JavaScript. It uses the same design as React, letting you compose a rich mobile UI from declarative components.

```
import React, { Component } from 'react';
import { Text, View } from 'react-native';

class WhyReactNativeIsSoGreat extends Component {
  render() {
    return (
      <View>
        <Text>
          If you like React on the web, you'll like React Native.
        </Text>
        <Text>
          You just use native components like 'View' and 'Text',
          instead of web components like 'div' and 'span'.
        </Text>
      </View>
    );
  }
}
```

##  A React Native app is a real mobile app

With React Native, you don't build a "mobile web app", an "HTML5 app", or a "hybrid app". You build a real mobile app that's indistinguishable from an app built using Objective-C or Java. React Native uses the same fundamental UI building blocks as regular iOS and Android apps. You just put those building blocks together using JavaScript and React.

```
import React, { Component } from 'react';
import { Image, ScrollView, Text } from 'react-native';

class AwkwardScrollingImageWithText extends Component {
  render() {
    return (
      <ScrollView>
        <Image source={{uri: 'https://i.chzbgr.com/full/7345954048/h7E2C65F9/'}} />
        <Text>
          On iOS, a React Native ScrollView uses a native UIScrollView.
          On Android, it uses a native ScrollView.

          On iOS, a React Native Image uses a native UIImageView.
          On Android, it uses a native ImageView.

          React Native wraps the fundamental native components, giving you
          the performance of a native app, plus the clean design of React.
        </Text>
      </ScrollView>
    );
  }
}
```

##  Don't waste time recompiling

React Native lets you build your app faster. Instead of recompiling, you can reload your app instantly. With hot reloading, you can even run new code while retaining your application state. Give it a try - it's a magical experience.

##  Use native code when you need to

React Native combines smoothly with components written in Objective-C, Java, or Swift. It's simple to drop down to native code if you need to optimize a few aspects of your application. It's also easy to build part of your app in React Native, and part of your app using native code directly - that's how the Facebook app works.

```
import React, { Component } from 'react';
import { Text, View } from 'react-native';
import { TheGreatestComponentInTheWorld } from './your-native-code';

class SomethingFast extends Component {
  render() {
    return (
      <View>
        <TheGreatestComponentInTheWorld />
        <Text>
          TheGreatestComponentInTheWorld could use native Objective-C,
          Java, or Swift - the product development process is the same.
        </Text>
      </View>
    );
  }
}

```


text:
Deep in Code Jam's secret algorithm labs, we devote countless hours to wrestling with one of the most complex problems of our time: efficiently sorting a list of integers into non-decreasing order. We have taken a careful look at the classic bubble sort algorithm, and we are pleased to announce a new variant.

The basic operation of the standard bubble sort algorithm is to examine a pair of adjacent numbers, and reverse that pair if the left number is larger than the right number. But our algorithm examines a group of three adjacent numbers, and if the leftmost number is larger than the rightmost number, it reverses that entire group. Because our algorithm is a "triplet bubble sort", we have named it Trouble Sort for short.

```
  TroubleSort(L): // L is a 0-indexed list of integers
    let done := false
    while not done:
      done = true
      for i := 0; i < len(L)-2; i++:
        if L[i] > L[i+2]:
          done = false
          reverse the sublist from L[i] to L[i+2], inclusive
```

For example, for L = 5 6 6 4 3, Trouble Sort would proceed as follows:

* First pass:
  * inspect 5 6 6, do nothing: 5 6 6 4 3
  * inspect 6 6 4, see that 6 > 4, reverse the triplet: 5 4 6 6 3
  * inspect 6 6 3, see that 6 > 3, reverse the triplet: 5 4 3 6 6
* Second pass:
  * inspect 5 4 3, see that 5 > 3, reverse the triplet: 3 4 5 6 6
  * inspect 4 5 6, do nothing: 3 4 5 6 6
  * inspect 5 6 6, do nothing: 3 4 5 6 6
* Then the third pass inspects the three triplets and does nothing, so the algorithm terminates.

We were looking forward to presenting Trouble Sort at the Special Interest Group in Sorting conference in Hawaii, but one of our interns has just pointed out a problem: it is possible that Trouble Sort does not correctly sort the list! Consider the list 8 9 7, for example.

We need your help with some further research. Given a list of N integers, determine whether Trouble Sort will successfully sort the list into non-decreasing order. If it will not, find the index (counting starting from 0) of the first sorting error after the algorithm has finished: that is, the first value that is larger than the value that comes directly after it when the algorithm is done.

More details: https://codingcompetitions.withgoogle.com/codejam/round/00000000000000cb/00000000000079cb


text:
import org.apache.hadoop.hbase.{HBaseConfiguration, HTableDescriptor, HColumnDescriptor, TableName}
import org.apache.hadoop.hbase.client.{Admin, Connection, ConnectionFactory, Table, Put}
import org.apache.hadoop.hbase.mapreduce.TableInputFormat

import org.apache.spark._
import org.apache.spark.SparkContext._
import org.apache.spark.rdd._
import org.apache.spark.input._
import scala.sys.process._
import scala.collection.JavaConverters._

import org.apache.commons.io.FilenameUtils
import org.apache.commons.io.FileUtils.{deleteDirectory, readFileToByteArray, writeByteArrayToFile}
import org.apache.commons.io.filefilter.PrefixFileFilter

import java.util.{UUID}
import java.io.{File, FileFilter, FileWriter, IOException}
import java.nio.file.Paths
import java.lang.System.{getenv}

object Util {

  /* shamelessly adapted from:
   * https://github.com/apache/spark/blob/master/core/src/main/scala/org/apache/spark/util/Utils.scala
   */

  val MAX_DIR_CREATION_ATTEMPTS: Int = 10

  def createTempDir(
	    root: String = System.getProperty("java.io.tmpdir"),
	    namePrefix: String = "nbis"): File = {

    var attempts = 0
    val maxAttempts = MAX_DIR_CREATION_ATTEMPTS
    var dir: File = null
    while (dir == null) {
      attempts += 1
      if (attempts >= maxAttempts) {
	throw new IOException("Failed to create a temp directory (under %s) after %s attempts"
			      .format(root, maxAttempts))
      }
      try {
	dir = new File(root, namePrefix + "-" + UUID.randomUUID.toString)
	if (dir.exists() || !dir.mkdirs()) {
	  dir = null
	}
      } catch { case e: SecurityException => dir = null; }
    }
    dir.getCanonicalFile
  }
}

object MINDTCT {

  type FilePath = String
  type FileExtension = String
  type MindtctResult = Array[(FileExtension, Array[Byte])]

  def run_mindtct(item: (FilePath, PortableDataStream)): (FilePath, MindtctResult) = {
    val workarea = Util.createTempDir()
    val resultPrefix = Paths.get(workarea.getAbsolutePath, "out").toFile()
    val datafile = File.createTempFile("mindtct_input", null, workarea)

    try {
      writeByteArrayToFile(datafile, item._2.toArray())
      val mindtct = Seq("mindtct", datafile.getAbsolutePath, resultPrefix.getAbsolutePath)
      val returncode = mindtct.!
      val results = workarea.listFiles(new PrefixFileFilter("out"): FileFilter)
      		    .map {
      			  file =>
      			  val ext  = FilenameUtils.getExtension(file.getName)
      			  val bits = readFileToByteArray(file)
      			  (ext, bits)
      			 }
      (item._1, results)
    } finally {
      datafile.delete()
      deleteDirectory(workarea)
    }

  }

  def hbaseConnection(): Connection = {
    val cfg = HBaseConfiguration.create()
    ConnectionFactory.createConnection(cfg)
  }

  def hbaseTableName(): TableName = {
    TableName.valueOf("mindtct")
  }

  def getPut(item: (FilePath, MindtctResult)): Put = {
    val row = new Put(item._1.getBytes)
    item._2.foreach(
      ext_bits =>
      row.addColumn("output".getBytes,
		    ext_bits._1.getBytes,
		    ext_bits._2)
    )
    row
  }

  def main(args: Array[String]) {
    val conf = new SparkConf().setAppName("MINDTCT")
    val sc = new SparkContext(conf)

    val ha = hbaseConnection.getAdmin
    val htable = new HTableDescriptor(hbaseTableName)
    if (! ha.tableExists(hbaseTableName)) {
      htable.addFamily(new HColumnDescriptor("output"))
      ha.createTable(htable)
    }

    val pngFiles = sc.binaryFiles("hdfs:///nist/NISTSpecialDatabase4GrayScaleImagesofFIGS/sd04/png_txt/figs_0")
    		  .filter(_._1.endsWith(".png"))
    val nfiles = pngFiles.count()
    println("nfiles: %s".format(nfiles))
    val mindtctResults = pngFiles.map(run_mindtct)
    val puts = mindtctResults.map(getPut)
    puts.foreachPartition{
      iterOfPut =>
      val conn = hbaseConnection
      try {
	val table = conn.getTable(hbaseTableName)
	table.put(iterOfPut.toList.asJava)
      } finally { conn.close }
    }

  }
}


text:
/**
 *     This code was auto-generated by a tool.
 *
 *     Changes to this file may cause incorrect behavior and will be lost if
 *     the code is regenerated.
*/
package com.mozu.test.framework.datafactory;

import java.util.List;
import java.util.ArrayList;
import org.apache.http.HttpStatus;
import com.mozu.api.ApiException;
import com.mozu.api.ApiContext;
import com.mozu.test.framework.core.TestFailException;
import com.mozu.api.resources.commerce.settings.general.CustomRouteSettingsResource;

/** <summary>
 * Use the Custom Routes resource to manage your custom route settings. Custom routing allows you to display SEO-friendly URLs on your site that map behind-the-scenes to conventional Mozu resources such as a product page or a search results page. With custom routing, you gain advanced control over the URL structures on your site and can more visibly highlight the products or categories your shoppers are interested in purchasing.To learn more about custom routing, refer to the [Custom Route Settings](../../../guides/settings/custom-routes.htm) topic.
 * </summary>
 */
public class CustomRouteSettingsFactory
{

	public static com.mozu.api.contracts.sitesettings.general.general.routing.CustomRouteSettings getCustomRouteSettings(ApiContext apiContext, int expectedCode) throws Exception
	{
		return getCustomRouteSettings(apiContext,  null, expectedCode);
	}

	public static com.mozu.api.contracts.sitesettings.general.general.routing.CustomRouteSettings getCustomRouteSettings(ApiContext apiContext, String responseFields, int expectedCode) throws Exception
	{
		com.mozu.api.contracts.sitesettings.general.general.routing.CustomRouteSettings returnObj = new com.mozu.api.contracts.sitesettings.general.general.routing.CustomRouteSettings();
		CustomRouteSettingsResource resource = new CustomRouteSettingsResource(apiContext);
		try
		{
			returnObj = resource.getCustomRouteSettings( responseFields);
		}
		catch (ApiException e)
		{
			if(e.getHttpStatusCode() != expectedCode)
				throw new TestFailException("" + e.getHttpStatusCode(), Thread.currentThread().getStackTrace()[2].getMethodName(), "" + expectedCode, "");
			else
				return null;
		}
		if(expectedCode != 304 && !(expectedCode >= 200 && expectedCode <= 300) && !(expectedCode == HttpStatus.SC_NOT_FOUND && returnObj == null))
			throw new TestFailException("304 or between 200 and 300", Thread.currentThread().getStackTrace()[2].getMethodName(), "" + expectedCode, "");
		return returnObj;
	}

	public static com.mozu.api.contracts.sitesettings.general.general.routing.CustomRouteSettings createCustomRouteSettings(ApiContext apiContext, com.mozu.api.contracts.sitesettings.general.general.routing.CustomRouteSettings settings, int expectedCode) throws Exception
	{
		return createCustomRouteSettings(apiContext,  settings,  null, expectedCode);
	}

	public static com.mozu.api.contracts.sitesettings.general.general.routing.CustomRouteSettings createCustomRouteSettings(ApiContext apiContext, com.mozu.api.contracts.sitesettings.general.general.routing.CustomRouteSettings settings, String responseFields, int expectedCode) throws Exception
	{
		com.mozu.api.contracts.sitesettings.general.general.routing.CustomRouteSettings returnObj = new com.mozu.api.contracts.sitesettings.general.general.routing.CustomRouteSettings();
		CustomRouteSettingsResource resource = new CustomRouteSettingsResource(apiContext);
		try
		{
			returnObj = resource.createCustomRouteSettings( settings,  responseFields);
		}
		catch (ApiException e)
		{
			if(e.getHttpStatusCode() != expectedCode)
				throw new TestFailException("" + e.getHttpStatusCode(), Thread.currentThread().getStackTrace()[2].getMethodName(), "" + expectedCode, "");
			else
				return null;
		}
		if(expectedCode != 304 && !(expectedCode >= 200 && expectedCode <= 300) && !(expectedCode == HttpStatus.SC_NOT_FOUND && returnObj == null))
			throw new TestFailException("304 or between 200 and 300", Thread.currentThread().getStackTrace()[2].getMethodName(), "" + expectedCode, "");
		return returnObj;
	}

	public static com.mozu.api.contracts.sitesettings.general.general.routing.CustomRouteSettings updateCustomRouteSettings(ApiContext apiContext, com.mozu.api.contracts.sitesettings.general.general.routing.CustomRouteSettings settings, int expectedCode) throws Exception
	{
		return updateCustomRouteSettings(apiContext,  settings,  null, expectedCode);
	}

	public static com.mozu.api.contracts.sitesettings.general.general.routing.CustomRouteSettings updateCustomRouteSettings(ApiContext apiContext, com.mozu.api.contracts.sitesettings.general.general.routing.CustomRouteSettings settings, String responseFields, int expectedCode) throws Exception
	{
		com.mozu.api.contracts.sitesettings.general.general.routing.CustomRouteSettings returnObj = new com.mozu.api.contracts.sitesettings.general.general.routing.CustomRouteSettings();
		CustomRouteSettingsResource resource = new CustomRouteSettingsResource(apiContext);
		try
		{
			returnObj = resource.updateCustomRouteSettings( settings,  responseFields);
		}
		catch (ApiException e)
		{
			if(e.getHttpStatusCode() != expectedCode)
				throw new TestFailException("" + e.getHttpStatusCode(), Thread.currentThread().getStackTrace()[2].getMethodName(), "" + expectedCode, "");
			else
				return null;
		}
		if(expectedCode != 304 && !(expectedCode >= 200 && expectedCode <= 300) && !(expectedCode == HttpStatus.SC_NOT_FOUND && returnObj == null))
			throw new TestFailException("304 or between 200 and 300", Thread.currentThread().getStackTrace()[2].getMethodName(), "" + expectedCode, "");
		return returnObj;
	}

	public static void deleteCustomRouteSettings(ApiContext apiContext, int expectedCode) throws Exception
	{
		CustomRouteSettingsResource resource = new CustomRouteSettingsResource(apiContext);
		try
		{
			resource.deleteCustomRouteSettings();
		}
		catch (ApiException e)
		{
			if(e.getHttpStatusCode() != expectedCode)
				throw new TestFailException("" + e.getHttpStatusCode(), Thread.currentThread().getStackTrace()[2].getMethodName(), "" + expectedCode, "");
			else
				return;
		}
		if(expectedCode != 304 && !(expectedCode >= 200 && expectedCode <= 300))
			throw new TestFailException("304 or between 200 and 300", Thread.currentThread().getStackTrace()[2].getMethodName(), "" + expectedCode, "");
	}

}


text:
Store is an Observable injected into a component

### Class definition
```
export class SomeComponent {
    players: Observable<String[]>;
    constructor(private store:Store<AppState>) {
        this.players = store.select(state => state.players);
    }
}
```

### Unravel subscription
```
<ol>
    <li *ngFor="player of players | async"> {{player}} </li>
</ol>
```


text:
'use strict';

//Setting up route
angular.module('subscribers').config(['$stateProvider',
	function($stateProvider) {
		// Subscribers state routing
		$stateProvider.
		state('listSubscribers', {
			url: '/subscribers',
			templateUrl: 'modules/subscribers/views/list-subscribers.client.view.html'
		}).
		state('createSubscriber', {
			url: '/subscribers/create',
			templateUrl: 'modules/subscribers/views/create-subscriber.client.view.html'
		}).
		state('viewSubscriber', {
			url: '/subscribers/:subscriberId',
			templateUrl: 'modules/subscribers/views/view-subscriber.client.view.html'
		}).
		state('editSubscriber', {
			url: '/subscribers/:subscriberId/edit',
			templateUrl: 'modules/subscribers/views/edit-subscriber.client.view.html'
		});
	}
]);


text:
from .application import Application

# Initialize default app
# Only for use within *this* module.
# One should always call use_app() to obtain the default app.
default_app = None

def use_app(backend_name=None, call_reuse=True):
    """ Get/create the default Application object

    It is safe to call this function multiple times, as long as
    backend_name is None or matches the already selected backend.

    Parameters
    ----------
    backend_name : str | None
        The name of the backend application to use. If not specified, Vispy
        tries to select a backend automatically. See ``vispy.use()`` for
        details.
    call_reuse : bool
        Whether to call the backend's `reuse()` function (True by default).
        Not implemented by default, but some backends need it. For example,
        the notebook backends need to inject some JavaScript in a notebook as
        soon as `use_app()` is called.

    """
    global default_app

    # If we already have a default_app, raise error or return
    if default_app is not None:
        names = default_app.backend_name.lower().replace('(', ' ').strip(') ')
        names = [name for name in names.split(' ') if name]
        if backend_name and backend_name.lower() not in names:
            raise RuntimeError('Can only select a backend once, already using '
                               '%s.' % names)
        else:
            if call_reuse:
                default_app.reuse()
            return default_app  # Current backend matches backend_name

    # Create default app
    default_app = Application(backend_name)
    return default_app

def create():
    """Create the native application.
    """
    use_app(call_reuse=False)
    return default_app.create()

def run():
    """Enter the native GUI event loop.
    """
    use_app(call_reuse=False)
    return default_app.run()

def quit():
    """Quit the native GUI event loop.
    """
    use_app(call_reuse=False)
    return default_app.quit()

def process_events():
    """Process all pending GUI events

    If the mainloop is not running, this should be done regularly to
    keep the visualization interactive and to keep the event system going.
    """
    use_app(call_reuse=False)
    return default_app.process_events()


text:
package org.prevayler;

import org.prevayler.implementation.PrevaylerImpl;

/**
 * Created by thma on 13.06.2016.
 */
public class PrevaylerFactory<P> {

    private P _prevalentSystem;

    public PrevaylerFactory(P newPrevalentSystem) {
        _prevalentSystem = newPrevalentSystem;
    }

    public static <P> Prevayler<P> createPrevayler(P newPrevalentSystem, String prevalenceBase) throws Exception {
        return new PrevaylerImpl<P>(newPrevalentSystem);
    }

    public static <P> Prevayler<P> createPrevayler(P newPrevalentSystem) throws Exception {
        return createPrevayler(newPrevalentSystem, "PrevalenceBase");
    }

    public Prevayler<P> create() throws Exception {
        return new PrevaylerImpl<P>(_prevalentSystem);
    }
}


text:
import Ember from 'ember';

export default Ember.Route.extend({
  afterModel: function() {
    var store = this.get('store')

    var promises = [
      store.findAll('invoice-line'),
      store.findAll('invoice'),
      store.findAll('customer')
    ]

    return Ember.RSVP.all(promises);
  },

  actions: {
    deleteInvoice(invoice) {
      let confirmation = confirm('Are you sure?');

      if (confirmation) {
        let customer = invoice.belongsTo('customer').value();

        customer.get('invoices').removeObject(invoice).then(
          () => {
            customer.save().then(
              () => {
                invoice.destroyRecord();
              }
            )
          }
        )
      }
    },

    markInvoicePaid(invoice) {
      invoice.paid = true;
      invoice.save();
    },

    markInvoiceUnpaid(invoice) {
      let confirmation = confirm('Mark invoice unpaid?');

      if (confirmation) {
        invoice.paid = false;
        invoice.save();
      }
    },

    // Save a new or modified invoice record
    saveInvoice(invoice) {
      // The 'save' button should only enable when the model is valid,
      // but better safe than sorry
      invoice.validate()
        .then(({ validations }) => {
          if (validations.get('isValid')) {
            // It is safe to save the child record first here as
            // there are no valid states in which the parent (customer)
            // is unsaved
            invoice.save().then(
              invoice => {
                // Get the customer record via the belongsTo reference
                let customer = invoice.belongsTo('customer').value();

                // If the customer's array already has a record with the
                // invoice's ID it will be updated rather than duplicated
                customer.get('invoices').pushObject(invoice)

                customer.save().then(
                  () => this.transitionTo('invoices.view', invoice.id)
                )
              },
              error => {
                alert(error)
                // FIXME: Display errors properly
              }
            )
          } else {
            // FIXME: Display errors properly
            alert("Please fix errors before saving.")
          }
        })
    },
  }
});


text:
define([
    'app/clipboard',

    'dijit/_TemplatedMixin',
    'dijit/_WidgetBase',
    'dijit/_WidgetsInTemplateMixin',

    'dojo/aspect',
    'dojo/dom-construct',
    'dojo/text!./templates/AttributeCopyPaste.html',
    'dojo/_base/array',
    'dojo/_base/declare',
    'dojo/_base/lang',

    'dijit/form/Button'
], function (
    clipboard,

    _TemplatedMixin,
    _WidgetBase,
    _WidgetsInTemplateMixin,

    aspect,
    domConstruct,
    template,
    array,
    declare,
    lang
) {
    return declare([_WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin], {
        // description:
        //      Copy and paste form data

        templateString: template,
        baseClass: 'attribute-copy-paste atiNavButtons',
        widgetsInTemplate: true,

        // dijit reference to copy button
        copyButton: null,

        // dijit reference to paste button
        pasteButton: null,

        // the attribute name mapped to it's dijit
        fieldInfoMap: {},

        // ignoreFields: []
        // summary:
        //      fields to ignore with copy paste
        ignoreFields: ['OBJECTID', 'Editor', 'LoadDate', 'ModifyDate', 'AddNum', 'AddNumSuffix'],

        // Properties to be sent into constructor

        attributeEditor: null,

        // featureLayer: esri/layer/FeatureLayer
        // summary:
        //      the feature layer used in the attribute editor
        featureLayer: null,

        _setClipboardAttr: function (value) {
            var clone = lang.clone(value);

            for (var prop in clone) {
                if (clone.hasOwnProperty(prop)) {
                    if (this.ignoreFields.indexOf(prop) > -1) {
                        delete clone[prop];
                    }
                }
            }
            clipboard.data = clone;
            // this is _clipboard because of the app\clipboard amd module
            this._set('_clipboard', clone);
        },

        _getClipboardAttr: function () {
            return clipboard.data;
        },

        postCreate: function () {
            // summary:
            //      Overrides method of same name in dijit._Widget.
            // tags:
            //      private
            console.log('app.AttributeCopyPaste::postCreate', arguments);

            this.setupConnections();
            this.inherited(arguments);

            domConstruct.place(this.domNode, this.attributeEditor.editButtons);
        },
        setupConnections: function () {
            // summary:
            //      wire events, and such
            //
            console.log('app.AttributeCopyPaste::setupConnections', arguments);

            var self = this;

            // selects the first feature
            this.featureLayer.on('selection-complete', function (evt) {
                if (!evt || !evt.features || evt.features.length < 1) {
                    return;
                }

                self.data = evt.features[0].attributes;
            });

            // updates the feature as the user uses the next buttons
            this.attributeEditor.on('next', function (evt) {
                if (!evt || !evt.feature) {
                    return;
                }

                self.data = evt.feature.attributes;
            });

            aspect.after(this.attributeEditor, '_updateSelection', function () {
                array.forEach(this.layerInfos[0].fieldInfos, function (item) {
                    if (self.ignoreFields.indexOf(item.fieldName) < 0) {
                        self.fieldInfoMap[item.fieldName] = item.dijit;
                    }
                }, self);
            }, true);

            // this is _clipboard because of the app\clipboard amd module
            this.watch('_clipboard', lang.hitch(this, function (field, old, value) {
                if (!value) {
                    return;
                }

                this.pasteButton.set('disabled', false);
            }));
        },
        copy: function () {
            // summary:
            //      copies whatever is in this.data to app.clipboard
            //
            console.log('app.AttributeCopyPaste::copy', arguments);

            this.set('clipboard', this.data);
        },
        paste: function () {
            // summary:
            //      pastes the form data from the `attributeEditor`
            console.log('app.AttributeCopyPaste::paste', arguments);

            var data = this.get('clipboard');

            if (!data) {
                return;
            }

            for (var fieldName in this.fieldInfoMap) {
                if (this.fieldInfoMap.hasOwnProperty(fieldName)) {
                    var value = data[fieldName];
                    this._setDijitValue(fieldName, value);
                }
            }
        },
        _setDijitValue: function (fieldName, value) {
            // summary:
            //      updates the field info dijit
            // fieldName - the data attribute field name
            // value - the string value to set as the new value
            console.log('app.AttributeCopyPaste::_setDijitValue', arguments);

            array.forEach(this.attributeEditor.layerInfos[0].fieldInfos, function (item) {
                if (item.fieldName === fieldName) {
                    item.dijit.set('value', value);
                }
            });
        }
    });
});


text:
'use strict';

var moment = require('moment-timezone');
var should = require('chai').should(); // eslint-disable-line
var sinon = require('sinon');

describe('date', function() {
  var Hexo = require('../../../lib/hexo');
  var hexo = new Hexo();
  var dateHelper = require('../../../lib/plugins/helper/date');
  var clock;

  before(function() {
    clock = sinon.useFakeTimers(Date.now());
  });

  after(function() {
    clock.restore();
  });

  it('date', function() {
    var ctx = {
      config: hexo.config,
      page: {}
    };

    var date = dateHelper.date.bind(ctx);

    // now
    date().should.eql(moment().format(hexo.config.date_format));

    // moment
    date(moment()).should.eql(moment().format(hexo.config.date_format));
    date(moment(), 'MMM-D-YYYY').should.eql(moment().format('MMM-D-YYYY'));

    // date
    date(new Date()).should.eql(moment().format(hexo.config.date_format));
    date(new Date(), 'MMM-D-YYYY').should.eql(moment().format('MMM-D-YYYY'));

    // number
    date(Date.now()).should.eql(moment().format(hexo.config.date_format));
    date(Date.now(), 'MMM-D-YYYY').should.eql(moment().format('MMM-D-YYYY'));

    // page.lang
    ctx.page.lang = 'zh-tw';
    date(Date.now(), 'MMM D YYYY').should.eql(moment().locale('zh-tw').format('MMM D YYYY'));
    ctx.page.lang = '';

    // config.language
    ctx.config.language = 'ja';
    date(Date.now(), 'MMM D YYYY').should.eql(moment().locale('ja').format('MMM D YYYY'));
    ctx.config.language = '';

    // timezone
    ctx.config.timezone = 'UTC';
    date(Date.now(), 'LLL').should.eql(moment().tz('UTC').format('LLL'));
    ctx.config.timezone = '';
  });

  it('date_xml', function() {
    var dateXML = dateHelper.date_xml;

    // now
    dateXML().should.eql(moment().toISOString());

    // moment
    dateXML(moment()).should.eql(moment().toISOString());

    // date
    dateXML(new Date()).should.eql(moment().toISOString());

    // number
    dateXML(Date.now()).should.eql(moment().toISOString());
  });

  it('relative_date', function() {
    var ctx = {
      config: hexo.config,
      page: {}
    };

    var relativeDate = dateHelper.relative_date.bind(ctx);

    // now
    relativeDate().should.eql(moment().fromNow());

    // moment
    relativeDate(moment()).should.eql(moment().fromNow());

    // date
    relativeDate(new Date()).should.eql(moment().fromNow());

    // number
    relativeDate(Date.now()).should.eql(moment().fromNow());
  });

  it('time', function() {
    var ctx = {
      config: hexo.config,
      page: {}
    };

    var time = dateHelper.time.bind(ctx);

    // now
    time().should.eql(moment().format(hexo.config.time_format));

    // moment
    time(moment()).should.eql(moment().format(hexo.config.time_format));
    time(moment(), 'H:mm').should.eql(moment().format('H:mm'));

    // date
    time(new Date()).should.eql(moment().format(hexo.config.time_format));
    time(new Date(), 'H:mm').should.eql(moment().format('H:mm'));

    // number
    time(Date.now()).should.eql(moment().format(hexo.config.time_format));
    time(Date.now(), 'H:mm').should.eql(moment().format('H:mm'));

    // page.lang
    ctx.page.lang = 'zh-tw';
    time(Date.now(), 'A H:mm').should.eql(moment().locale('zh-tw').format('A H:mm'));
    ctx.page.lang = '';

    // config.language
    ctx.config.language = 'ja';
    time(Date.now(), 'A H:mm').should.eql(moment().locale('ja').format('A H:mm'));
    ctx.config.language = '';

    // timezone
    ctx.config.timezone = 'UTC';
    time().should.eql(moment().tz('UTC').format(hexo.config.time_format));
    ctx.config.timezone = '';
  });

  it('full_date', function() {
    var ctx = {
      config: hexo.config,
      date: dateHelper.date,
      time: dateHelper.time,
      page: {}
    };

    var fullDate = dateHelper.full_date.bind(ctx);
    var fullDateFormat = hexo.config.date_format + ' ' + hexo.config.time_format;

    // now
    fullDate().should.eql(moment().format(fullDateFormat));

    // moment
    fullDate(moment()).should.eql(moment().format(fullDateFormat));
    fullDate(moment(), 'MMM-D-YYYY').should.eql(moment().format('MMM-D-YYYY'));

    // date
    fullDate(new Date()).should.eql(moment().format(fullDateFormat));
    fullDate(new Date(), 'MMM-D-YYYY').should.eql(moment().format('MMM-D-YYYY'));

    // number
    fullDate(Date.now()).should.eql(moment().format(fullDateFormat));
    fullDate(Date.now(), 'MMM-D-YYYY').should.eql(moment().format('MMM-D-YYYY'));

    // page.lang
    ctx.page.lang = 'zh-tw';
    fullDate(Date.now(), 'LLL').should.eql(moment().locale('zh-tw').format('LLL'));
    ctx.page.lang = '';

    // config.language
    ctx.config.language = 'ja';
    fullDate(Date.now(), 'LLL').should.eql(moment().locale('ja').format('LLL'));
    ctx.config.language = '';

    // timezone
    ctx.config.timezone = 'UTC';
    fullDate().should.eql(moment().tz('UTC').format(fullDateFormat));
    ctx.config.timezone = '';
  });

  it('time_tag', function() {
    var ctx = {
      config: hexo.config,
      date: dateHelper.date,
      page: {}
    };

    var timeTag = dateHelper.time_tag.bind(ctx);

    function result(date, format) {
      date = date || new Date();
      format = format || hexo.config.date_format;
      return '<time datetime="' + moment(date).toISOString() + '">' + moment(date).format(format) + '</time>';
    }

    function check(date, format) {
      format = format || hexo.config.date_format;
      timeTag(date, format).should.eql(result(date, format));
    }

    // now
    timeTag().should.eql(result());

    // moment
    check(moment());
    check(moment(), 'MMM-D-YYYY');

    // date
    check(new Date());
    check(new Date(), 'MMM-D-YYYY');

    // number
    check(Date.now());
    check(Date.now(), 'MMM-D-YYYY');

    // page.lang
    ctx.page.lang = 'zh-tw';
    timeTag(Date.now(), 'LLL').should.eql('<time datetime="' + moment().toISOString() + '">' + moment().locale('zh-tw').format('LLL') + '</time>');
    ctx.page.lang = '';

    // config.language
    ctx.config.language = 'ja';
    timeTag(Date.now(), 'LLL').should.eql('<time datetime="' + moment().toISOString() + '">' + moment().locale('ja').format('LLL') + '</time>');
    ctx.config.language = '';

    // timezone
    ctx.config.timezone = 'UTC';
    timeTag(Date.now(), 'LLL').should.eql('<time datetime="' + moment().toISOString() + '">' + moment().tz('UTC').format('LLL') + '</time>');
    ctx.config.timezone = '';
  });
});


text:
package br.com.cmabreu.webomt.action;

import java.util.List;

import javax.xml.bind.DatatypeConverter;

import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.InterceptorRef;
import org.apache.struts2.convention.annotation.ParentPackage;
import org.apache.struts2.convention.annotation.Result;

import br.com.cmabreu.webomt.ieee1516e.ObjectModelType;
import br.com.cmabreu.webomt.misc.PathFinder;
import br.com.cmabreu.webomt.parser.FOMObjectCreator;
import br.com.cmabreu.webomt.persistence.entity.Federation;
import br.com.cmabreu.webomt.persistence.services.FederationService;
import br.com.cmabreu.webomt.wrapper.FomObject;
import br.com.cmabreu.webomt.wrapper.FomWrapper;

@Action (value = "showFomFile", results = { @Result (location = "fomfile.jsp", name = "ok"),
		@Result (location = "fomfileAlt.jsp", name = "alternate") } ,
		interceptorRefs= { @InterceptorRef("seguranca")	 } )

@ParentPackage("default")
public class ShowFomFileAction extends BasicActionClass {
	private int idFederation;
	private Federation federation;
	private ObjectModelType omt;
	private String glyphImage;
	private List<FomObject> objects;
	private String pageType;

	public String execute () {

		String retorno = "ok";
		if( pageType != null && pageType.equals("alternative")){
			retorno = "alternate";
		}

		try {

			FederationService fs = new FederationService();
			federation = fs.getFederation(idFederation);

			FOMObjectCreator foc = new FOMObjectCreator();
			String fomFile = PathFinder.getInstance().getPath() + "/foms/" + federation.getSerial() + "/" + federation.getDefinitionFile();

			omt = foc.generate(fomFile);

			FomWrapper fw = new FomWrapper( omt );
			objects = fw.getAllObjects();

			if ( omt.getModelIdentification().getGlyph() != null ) {
				byte[] bytes = omt.getModelIdentification().getGlyph().getValue();
				glyphImage = DatatypeConverter.printBase64Binary( bytes );
			}

		} catch ( Exception e ) {
			if ( e.getMessage() != null ) {
				setMessageText( "Error: " + e.getMessage() );
			} else {
				setMessageText( "Error: Cannot parse FOM file. Verify XML namespace (http://standards.ieee.org/IEEE1516-2010)." );
			}
		}

		return retorno;
	}

	public void setIdFederation(int idFederation) {
		this.idFederation = idFederation;
	}

	public Federation getFederation() {
		return federation;
	}

	public ObjectModelType getOmt() {
		return omt;
	}

	public String getGlyphImage() {
		return glyphImage;
	}

	public List<FomObject> getObjects() {
		return objects;
	}

	public void setPageType(String pageType) {
		this.pageType = pageType;
	}
}


text:
using System;
using Foundation;
using UIKit;
using MaterialComponents.MaterialCollections;
using MaterialComponents.MaterialFlexibleHeader;
using MaterialComponents.MaterialInk;
using MaterialComponents;

using Pesto.Model;
using System.Drawing;
using CoreGraphics;
using System.Linq;
using System.Security.Policy;
using MaterialComponents.MaterialShadowLayer;
using MaterialComponents.ShadowElevation;
using CoreAnimation;

namespace Pesto.Views
{
    [Register("PestoCollectionViewController")]
    public class PestoCollectionViewController : MDCCollectionViewController
    {
        static nfloat AnimationDuration = 0.33f;
        static nfloat CellHeight = 330f;
        static nfloat DefaultHeaderHeight = 240f;
        static nfloat Inset = 5f;
        static nfloat SmallHeaderHeight = 76f;

        MDCShadowLayer shadowLayer;
        nfloat logoScale;
        MDCInkTouchController inkTouchController;
        PestoData pestoData;

        UIView logoSmallView;
        UIView logoView;

        public MDCFlexibleHeaderShadowIntensityChangeBlock ShadowDelegate;

        static readonly NSString cellId = new NSString("PestoCardCollectionViewCell");

        public nfloat scrollOffsetY;
        public DidSelectCellDelegate Delegate;

        MDCFlexibleHeaderContainerViewController flexHeaderContainerVC;
        public MDCFlexibleHeaderContainerViewController FlexHeaderContainerVC
        {
            get => flexHeaderContainerVC;
            set
            {
                this.flexHeaderContainerVC = value;
                var headerView = this.flexHeaderContainerVC.HeaderViewController.HeaderView;
                headerView.TrackingScrollView = this.CollectionView;
                headerView.MaximumHeight = DefaultHeaderHeight;
                headerView.MinimumHeight = SmallHeaderHeight;
                headerView.AddSubview(PestoHeaderView());

                shadowLayer = new MDCShadowLayer();
                headerView.ShadowLayer = shadowLayer;
                headerView.SetShadowLayer(shadowLayer, ShadowDelegate);

                // MAR: Delegated Example - Using delegate
                // headerView.SetShadowLayer(shadowLayer, ShadowDelegate);

                // MAR: Delegated Example - using lambda
                //headerView.SetShadowLayer(shadowLayer, (CALayer layer, nfloat intensity) =>
                //{
                //    var elevation = Elevation.MDCShadowElevationAppBar ; // * intensity;
                //    (layer as MDCShadowLayer).Elevation = elevation;
                //});
            }
        }

        public void  ShadowIntensityChangeBlock(CALayer layer, nfloat intensity)
        {
            if (layer is MDCShadowLayer) {
                var elevation = Elevation.MDCShadowElevationAppBar * intensity;
                (layer as MDCShadowLayer).Elevation = elevation;
            }
        }

        [Export("initWithCollectionViewLayout:")]
        public PestoCollectionViewController(UICollectionViewLayout layout) : base (layout)
        {
            CollectionView.BackgroundColor = UIColor.White;
            CollectionView.RegisterClassForCell(typeof(PestoCardCollectionViewCell), cellId);

            pestoData = new PestoData();

        }

        public PestoCollectionViewController() : base() {

        }

        public override void ViewDidLoad()
        {
            base.ViewDidLoad();
            var styler = this.Styler;
            styler.CellStyle = MDCCollectionViewCellStyle.Card;
            styler.CellLayoutType = MDCCollectionViewCellLayoutType.Grid;
            styler.GridPadding = Inset;

            if (View.Frame.Size.Width < View.Frame.Size.Height) {
                styler.GridColumnCount = 1;
            } else {
                styler.GridColumnCount = 2;
            }
        }

        public override void ViewWillTransitionToSize(CoreGraphics.CGSize toSize, IUIViewControllerTransitionCoordinator coordinator)
        {
            //base.ViewWillTransitionToSize(toSize, coordinator);
            if (toSize.Width < toSize.Height) {
                Styler.GridColumnCount = 1;
            } else {
                Styler.GridColumnCount = 2;
            }
            CollectionView.CollectionViewLayout.InvalidateLayout();
            CenterHeaderWithSize(toSize);

        }

        public override void ViewWillAppear(bool animated)
        {
            base.ViewWillAppear(animated);
            CollectionView.CollectionViewLayout.InvalidateLayout();
            CenterHeaderWithSize(View.Frame.Size);
        }

        [Export("collectionView:didSelectItemAtIndexPath:")]
        public override void DidSelectItemAtIndexPath(UICollectionView collectionView, NSIndexPath indexPath) {
            base.DidSelectItemAtIndexPath(collectionView, indexPath);
            var cell = this.GetCell(collectionView, indexPath);
            //FIXME: this.
            if (cell is PestoCardCollectionViewCell)
            {
                this.Delegate(cell as PestoCardCollectionViewCell, () => { });
            }
            Console.WriteLine("DidSelectItemAtIndexPath\n");
        }

        [Export("collectionView:cellHeightAtIndexPath:")]
        public override nfloat CellHeightAtIndexPath(UICollectionView collectionView, NSIndexPath indexPath)
        {
            return CellHeight;
        }
        [Export("collectionView:cellForItemAtIndexPath:")]
        public override UICollectionViewCell GetCell(UICollectionView collectionView, NSIndexPath indexPath)
        {
            var cell = (PestoCardCollectionViewCell)collectionView.DequeueReusableCell(cellId, indexPath);
            if (cell == null)
            {
                return new UICollectionViewCell(CGRect.Empty);
            }

            var itemNum = indexPath.Row;
            NSUrl baseURL = new NSUrl(PestoData.DataBaseURL);

            var imageURLString = baseURL + pestoData.imageFileNames[itemNum];

            NSUrl imageURL = new NSUrl(imageURLString);

            var title = pestoData.titles[itemNum];
            var author = pestoData.authors[itemNum];
            var iconName = pestoData.iconNames[itemNum];

            cell.descText = pestoData.descriptions[itemNum];
            cell.PopulateContent(title, author, imageURL, iconName);

            return cell;
        }

        public override nint NumberOfSections(UICollectionView collectionView)
        {
            return 1; //pestoData.imageFileNames.Count();
        }

        public override nint GetItemsCount(UICollectionView collectionView, nint section)
        {
            return pestoData.imageFileNames.Count();
        }

        UIView PestoHeaderView()
        {
            var headerFrame = flexHeaderContainerVC.HeaderViewController.HeaderView.Bounds;
            var pestoHeaderView = new UIView(headerFrame);

            UIColor teal = new UIColor(0f, 0.67f, 0.55f, 1f);

            pestoHeaderView.BackgroundColor = teal;
            pestoHeaderView.Layer.MasksToBounds = true;
            pestoHeaderView.AutoresizingMask = UIViewAutoresizing.FlexibleWidth |
                UIViewAutoresizing.FlexibleHeight;

            var image = UIImage.FromBundle("PestoLogoLarge");
            logoView = new UIImageView(image);

            logoView.Center = new CGPoint(pestoHeaderView.Frame.Size.Width / 2f,
                                          pestoHeaderView.Frame.Size.Height / 2f);
            pestoHeaderView.AddSubview(logoView);

            var logoSmallImage = UIImage.FromBundle("PestoLogoSmall");
            logoSmallView = new UIImageView(logoSmallImage);

            logoSmallView.ContentMode = UIViewContentMode.ScaleAspectFill;
            logoSmallView.Layer.Opacity = 0f;

            pestoHeaderView.AddSubview(logoSmallView);

            inkTouchController = new MDCInkTouchController(pestoHeaderView);
            inkTouchController.AddInkView();
            return pestoHeaderView;
        }

        void CenterHeaderWithSize(CGSize size)
        {
            var statusBarHeight = UIApplication.SharedApplication.StatusBarFrame.Size.Height;
            var width = size.Width;

            var headerFrame = flexHeaderContainerVC.HeaderViewController.HeaderView.Bounds;
            if (logoView != null & logoSmallView != null)
            {
                logoView.Center = new CGPoint(width / 2f, headerFrame.Size.Height / 2f);
                logoSmallView.Center = new CGPoint(width / 2f,
                                                   (headerFrame.Size.Height - statusBarHeight) / 2f + statusBarHeight);
            }

        }

        // MARK: UIScrollViewDelegate
        public override void Scrolled(UIScrollView scrollView)
        {
            //base.Scrolled(scrollView);
            flexHeaderContainerVC.HeaderViewController.Scrolled(scrollView);
            scrollOffsetY = scrollView.ContentOffset.Y;
            if (logoView == null | logoSmallView == null) {
                return;
            }
            CenterHeaderWithSize(View.Frame.Size);
            logoScale = scrollView.ContentOffset.Y / -DefaultHeaderHeight;

            if (logoScale < 0.5f) {
                logoScale = 0.5f;
                UIView.AnimateNotify(AnimationDuration,
                                     0f,
                                     UIViewAnimationOptions.CurveEaseOut,
                                     () =>
                                     {
                                         logoView.Layer.Opacity = 0;
                                         logoSmallView.Layer.Opacity = 1f;
                                     },
                                     new UICompletionHandler((bool finished) => {}));
            } else {
                UIView.AnimateNotify(AnimationDuration,
                                     0f,
                                     UIViewAnimationOptions.CurveEaseOut,
                                     () =>
                                     {
                                         logoView.Layer.Opacity = 1f;
                                         logoSmallView.Layer.Opacity = 0;
                                     },
                                     new UICompletionHandler((bool finished) => { }));
            }

            logoView.Transform = CGAffineTransform.MakeScale(logoScale, logoScale);

        }

    }
}