<commit_before>package main

import (
	"code.google.com/p/gcfg"
	"flag"
	"github.com/anchor/picolog"
	"log"
	"os"
)

const (
	Version = "1.0.0"
)

var Logger *picolog.Logger

func main() {
	var cfg Config
	configFile := flag.String("cfg", "/etc/chevalier.gcfg", "Path to configuration file. This file should be in gcfg[0] format. [0] https://code.google.com/p/gcfg/")
	indexerMode := flag.Bool("index", false, "Start indexer mode.")
	indexOnce := flag.Bool("index-once", false, "Run indexer once and then exit.")
	readerMode := flag.Bool("read", false, "Start reader mode. Reader mode will invoke the indexer once on startup; use -no-index to disable.")
	noIndex := flag.Bool("no-index", false, "Do not index once at startup when started in reader mode.")
	logFile := flag.String("log-file", "", "If set, log to this file rather than stdout.")
	debug := flag.Bool("debug", false, "Enable debug logging (overrides configured log level).")
	flag.Parse()
	err := gcfg.ReadFileInto(&cfg, *configFile)
	if err != nil {
		log.Fatalf("Could not read config file at %v: %v", *configFile, err)
	}
	logLevel, err := picolog.ParseLogLevel(cfg.Chevalier.LogLevel)
	if err != nil {
		log.Fatalf("Could not parse log level: %v", err)
	}
	if *debug {
		logLevel = picolog.LogDebug
	}
	logStream := os.Stdout
	if *logFile != "" {
		logStream, err = os.OpenFile(*logFile, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0660)
		if err != nil {
			log.Fatalf("Could not open log file %v: %v", *logFile, err)
		}
	}
	Logger = picolog.NewLogger(logLevel, "chevalier", logStream)
	if *indexerMode {
		RunIndexer(cfg)
	} else if *readerMode {
		if !*noIndex {
			go RunIndexerOnce(cfg)
		}
		go RunStatus(cfg)
		RunReader(cfg)
	} else if *indexOnce {
		RunIndexerOnce(cfg)
	} else {
		Logger.Fatalf("Must specify one of -index, -read or -index-once.")
	}
}
<commit_msg>Basic signal handling in chevalierd process itself<commit_after>package main

import (
	"code.google.com/p/gcfg"
	"github.com/anchor/picolog"

	"flag"
	"log"
	"os"
	"os/signal"
	"syscall"
)

const (
	Version = "1.0.0"
)

var Logger *picolog.Logger

func handleInterrupts() {
	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, syscall.SIGHUP, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGWINCH)
	for {
		sig := <-sigs
		switch sig {
		case syscall.SIGHUP, syscall.SIGWINCH:
			Logger.Infof("Ignoring %v.", sig)
		case syscall.SIGQUIT, syscall.SIGINT, syscall.SIGTERM:
			Logger.Infof("Got %v, exiting.", sig)
			os.Exit(0)
		default:
			Logger.Fatalf("Got unexpected signal %v, panicking.")
		}
	}
}

func main() {
	var cfg Config
	configFile := flag.String("cfg", "/etc/chevalier.gcfg", "Path to configuration file. This file should be in gcfg[0] format. [0] https://code.google.com/p/gcfg/")
	indexerMode := flag.Bool("index", false, "Start indexer mode.")
	indexOnce := flag.Bool("index-once", false, "Run indexer once and then exit.")
	readerMode := flag.Bool("read", false, "Start reader mode. Reader mode will invoke the indexer once on startup; use -no-index to disable.")
	noIndex := flag.Bool("no-index", false, "Do not index once at startup when started in reader mode.")
	logFile := flag.String("log-file", "", "If set, log to this file rather than stdout.")
	debug := flag.Bool("debug", false, "Enable debug logging (overrides configured log level).")
	flag.Parse()
	err := gcfg.ReadFileInto(&cfg, *configFile)
	if err != nil {
		log.Fatalf("Could not read config file at %v: %v", *configFile, err)
	}
	logLevel, err := picolog.ParseLogLevel(cfg.Chevalier.LogLevel)
	if err != nil {
		log.Fatalf("Could not parse log level: %v", err)
	}
	if *debug {
		logLevel = picolog.LogDebug
	}
	logStream := os.Stdout
	if *logFile != "" {
		logStream, err = os.OpenFile(*logFile, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0660)
		if err != nil {
			log.Fatalf("Could not open log file %v: %v", *logFile, err)
		}
	}
	Logger = picolog.NewLogger(logLevel, "chevalier", logStream)
	go handleInterrupts()
	if *indexerMode {
		RunIndexer(cfg)
	} else if *readerMode {
		if !*noIndex {
			go RunIndexerOnce(cfg)
		}
		go RunStatus(cfg)
		RunReader(cfg)
	} else if *indexOnce {
		RunIndexerOnce(cfg)
	} else {
		Logger.Fatalf("Must specify one of -index, -read or -index-once.")
	}
}
<|endoftext|>


<commit_before>package main

import (
	"reflect"
	"testing"
)

var identifierTests = []struct {
	values map[string]map[string]string
	id     Identifier
	found  bool
	value  string
}{
	{
		values: nil,
		id:     Identifier{key: "foo"},
		found:  false,
	},
	{
		values: map[string]map[string]string{"": {"foo": "example"}},
		id:     Identifier{key: "foo"},
		found:  true,
		value:  "example",
	},
	{
		values: map[string]map[string]string{"": {"foo": "example"}},
		id:     Identifier{key: "bar"},
		found:  false,
	},
	{
		values: map[string]map[string]string{"example": {"foo": "example"}},
		id:     Identifier{scope: "example", key: "foo"},
		found:  true,
		value:  "example",
	},
	{
		values: map[string]map[string]string{"": {"foo": "example"}},
		id:     Identifier{scope: "example", key: "foo"},
		found:  false,
	},
	{
		values: map[string]map[string]string{"example": {"foo": "example"}},
		id:     Identifier{scope: "example", key: "bar"},
		found:  false,
	},
}

func Test_found(t *testing.T) {
	for _, test := range identifierTests {
		got := found(test.values, &test.id)
		if got != test.found {
			t.Errorf("found not correct for %+v (found: %+v, got: %+v)", test.id, test.found, got)
		}
	}
}

func Test_collect(t *testing.T) {
	ids := []*Identifier{
		&Identifier{scope: "foo", key: "foo"},
		&Identifier{scope: "foo", key: "bar"},
		&Identifier{scope: "zoo", key: "foo"},
		&Identifier{scope: "foo", key: "foo"},
		&Identifier{scope: "foo", key: "baz"},
		&Identifier{scope: "qux", key: "bar"},
	}
	expectedFoo := &IdentifierGroup{
		scope: "foo",
		keys:  []string{"foo", "bar", "baz"},
	}
	expectedBar := &IdentifierGroup{
		scope: "bar",
		keys:  nil,
	}
	idgFoo := collect(ids, "foo")
	if !reflect.DeepEqual(idgFoo, expectedFoo) {
		t.Errorf("collect not correct (expected: %+v, got: %+v)", expectedFoo, idgFoo)
	}
	idgBar := collect(ids, "bar")
	if !reflect.DeepEqual(idgBar, expectedBar) {
		t.Errorf("collect not correct (expected: %+v, got: %+v)", expectedBar, idgBar)
	}
}

func Test_insert(t *testing.T) {
	values := make(map[string]map[string]string)
	id := &Identifier{key: "foo"}
	value := "bar"
	insert(values, id, value)
	v, ok := values[""]["foo"]
	if !ok {
		t.Errorf("insert failed for %+v", id)
	}
	if v != value {
		t.Errorf("insert not correctly for %+v (found: %+v, got: %+v)", id, v, value)
	}
	id = &Identifier{scope: "foo", key: "bar"}
	value = "example"
	insert(values, id, value)
	v, ok = values["foo"]["bar"]
	if !ok {
		t.Errorf("insert failed for %+v", id)
	}
	if v != value {
		t.Errorf("insert not correctly for %+v (found: %+v, got: %+v)", id, v, value)
	}
}

func Test_empty(t *testing.T) {
	tests := []struct {
		values   map[string]map[string]string
		expected bool
	}{
		{
			values:   nil,
			expected: true,
		},
		{
			values: map[string]map[string]string{
				"foo": map[string]string{},
			},
			expected: true,
		},
		{
			values: map[string]map[string]string{
				"foo": map[string]string{
					"bar": "",
					"baz": "",
				},
			},
			expected: true,
		},
		{
			values: map[string]map[string]string{
				"foo": map[string]string{
					"bar": "",
					"baz": "",
				},
				"bar": map[string]string{
					"qux": "quux",
				},
			},
			expected: false,
		},
	}
	for _, test := range tests {
		got := empty(test.values)
		if got != test.expected {
			t.Errorf("empty not correctly for %+v (expected: %+v, got: %+v)", test.values, test.expected, got)
		}
	}
}

func Test_lookup(t *testing.T) {
	for _, test := range identifierTests {
		got := lookup(test.values, &test.id)
		if got != test.value {
			t.Errorf("lookup not correct for %+v (expected: %+v, got: %+v)", test.id, test.value, got)
		}
	}
}
<commit_msg>add tests for prompt of Identifier and IdentifierGroup<commit_after>package main

import (
	"reflect"
	"testing"
)

var identifierTests = []struct {
	values map[string]map[string]string
	id     Identifier
	found  bool
	value  string
}{
	{
		values: nil,
		id:     Identifier{key: "foo"},
		found:  false,
	},
	{
		values: map[string]map[string]string{"": {"foo": "example"}},
		id:     Identifier{key: "foo"},
		found:  true,
		value:  "example",
	},
	{
		values: map[string]map[string]string{"": {"foo": "example"}},
		id:     Identifier{key: "bar"},
		found:  false,
	},
	{
		values: map[string]map[string]string{"example": {"foo": "example"}},
		id:     Identifier{scope: "example", key: "foo"},
		found:  true,
		value:  "example",
	},
	{
		values: map[string]map[string]string{"": {"foo": "example"}},
		id:     Identifier{scope: "example", key: "foo"},
		found:  false,
	},
	{
		values: map[string]map[string]string{"example": {"foo": "example"}},
		id:     Identifier{scope: "example", key: "bar"},
		found:  false,
	},
}

func Test_prompt(t *testing.T) {
	id1 := &Identifier{key: "foo"}
	got := id1.prompt()
	if got != "foo: " {
		t.Errorf("prompt is not correct for %+v (found: %+v, got: %+v)", id1, "foo: ", got)
	}
	id2 := &Identifier{scope: "foo", key: "bar"}
	got = id2.prompt()
	if got != "[foo] bar: " {
		t.Errorf("prompt is not correct for %+v (found: %+v, got: %+v)", id2, "[foo] bar: ", got)
	}
	idg := &IdentifierGroup{scope: "foo", keys: []string{"bar", "baz", "qux"}}
	got = idg.prompt()
	if got != "[foo] bar, baz, qux: " {
		t.Errorf("prompt is not correct for %+v (found: %+v, got: %+v)", idg, "[foo] bar, baz, qux: ", got)
	}
}

func Test_found(t *testing.T) {
	for _, test := range identifierTests {
		got := found(test.values, &test.id)
		if got != test.found {
			t.Errorf("found not correct for %+v (found: %+v, got: %+v)", test.id, test.found, got)
		}
	}
}

func Test_collect(t *testing.T) {
	ids := []*Identifier{
		&Identifier{scope: "foo", key: "foo"},
		&Identifier{scope: "foo", key: "bar"},
		&Identifier{scope: "zoo", key: "foo"},
		&Identifier{scope: "foo", key: "foo"},
		&Identifier{scope: "foo", key: "baz"},
		&Identifier{scope: "qux", key: "bar"},
	}
	expectedFoo := &IdentifierGroup{
		scope: "foo",
		keys:  []string{"foo", "bar", "baz"},
	}
	expectedBar := &IdentifierGroup{
		scope: "bar",
		keys:  nil,
	}
	idgFoo := collect(ids, "foo")
	if !reflect.DeepEqual(idgFoo, expectedFoo) {
		t.Errorf("collect not correct (expected: %+v, got: %+v)", expectedFoo, idgFoo)
	}
	idgBar := collect(ids, "bar")
	if !reflect.DeepEqual(idgBar, expectedBar) {
		t.Errorf("collect not correct (expected: %+v, got: %+v)", expectedBar, idgBar)
	}
}

func Test_insert(t *testing.T) {
	values := make(map[string]map[string]string)
	id := &Identifier{key: "foo"}
	value := "bar"
	insert(values, id, value)
	v, ok := values[""]["foo"]
	if !ok {
		t.Errorf("insert failed for %+v", id)
	}
	if v != value {
		t.Errorf("insert not correctly for %+v (found: %+v, got: %+v)", id, v, value)
	}
	id = &Identifier{scope: "foo", key: "bar"}
	value = "example"
	insert(values, id, value)
	v, ok = values["foo"]["bar"]
	if !ok {
		t.Errorf("insert failed for %+v", id)
	}
	if v != value {
		t.Errorf("insert not correctly for %+v (found: %+v, got: %+v)", id, v, value)
	}
}

func Test_empty(t *testing.T) {
	tests := []struct {
		values   map[string]map[string]string
		expected bool
	}{
		{
			values:   nil,
			expected: true,
		},
		{
			values: map[string]map[string]string{
				"foo": map[string]string{},
			},
			expected: true,
		},
		{
			values: map[string]map[string]string{
				"foo": map[string]string{
					"bar": "",
					"baz": "",
				},
			},
			expected: true,
		},
		{
			values: map[string]map[string]string{
				"foo": map[string]string{
					"bar": "",
					"baz": "",
				},
				"bar": map[string]string{
					"qux": "quux",
				},
			},
			expected: false,
		},
	}
	for _, test := range tests {
		got := empty(test.values)
		if got != test.expected {
			t.Errorf("empty not correctly for %+v (expected: %+v, got: %+v)", test.values, test.expected, got)
		}
	}
}

func Test_lookup(t *testing.T) {
	for _, test := range identifierTests {
		got := lookup(test.values, &test.id)
		if got != test.value {
			t.Errorf("lookup not correct for %+v (expected: %+v, got: %+v)", test.id, test.value, got)
		}
	}
}
<|endoftext|>


<commit_before>package algorithm

//Stack
//author:Xiong Chuan Liang
//date:2015-1-30

import (
	"errors"
	"fmt"
)

type Stack struct {
	Element []interface{} //Element
}

func NewStack() *Stack {
	return &Stack{}
}

func (stack *Stack)Push(value ...interface{}){
	stack.Element = append(stack.Element,value...)
}

//从Stack移除元素
func (stack *Stack)Pop()(err error){
	if stack.Size()> 0 {
		stack.Element = stack.Element[:stack.Size() - 1]
		return nil
	}
	return errors.New("Stack为空.") //read empty stack
}

//返回下一个元素
func (stack *Stack)Top()(value interface{}){
	if stack.Size() > 0 {
		return stack.Element[stack.Size() - 1]
	}
	return nil //read empty stack
}

//交换值
func (stack *Stack)Swap(other *Stack){
	switch{
	case stack.Size() == 0 && other.Size() == 0:
		return
	case other.Size() == 0 :
		other.Element = stack.Element[:stack.Size()]
		stack.Element = nil
	case stack.Size()== 0 :
		stack.Element = other.Element
		other.Element = nil
	default:
		stack.Element,other.Element = other.Element,stack.Element
	}
	return
}

//修改指定索引的值
func (stack *Stack)Set(idx int,value interface{})(err error){
	if idx >= 0 && stack.Size() > 0 && stack.Size() > idx{
		stack.Element[idx] = value
		return nil
	}
	return errors.New("Set失败!")
}

//Stack的size
func (stack *Stack)Size()(int){
	return len(stack.Element)
}

//是否为空
func (stack *Stack)Empty()(bool){
	if stack.Size() > 0 {
		return true
	}
	return false
}

//打印
func (stack *Stack)Print(){
	for i := len(stack.Element) - 1; i >= 0; i--{
		fmt.Println(i,"=>",stack.Element[i])
	}
}
<commit_msg>Update stack.go<commit_after>package algorithm

//Stack
//author:Xiong Chuan Liang
//date:2015-1-30

import (
	"errors"
	"fmt"
)

type Stack struct {
	Element []interface{} //Element
}

func NewStack() *Stack {
	return &Stack{}
}

func (stack *Stack)Push(value ...interface{}){
	stack.Element = append(stack.Element,value...)
}

//从Stack移除元素
func (stack *Stack)Pop()(err error){
	if stack.Size()> 0 {
		stack.Element = stack.Element[:stack.Size() - 1]
		return nil
	}
	return errors.New("Stack为空.") //read empty stack
}

//返回下一个元素
func (stack *Stack)Top()(value interface{}){
	if stack.Size() > 0 {
		return stack.Element[stack.Size() - 1]
	}
	return nil //read empty stack
}

//交换值
func (stack *Stack)Swap(other *Stack){
	switch{
	case stack.Size() == 0 && other.Size() == 0:
		return
	case other.Size() == 0 :
		other.Element = stack.Element[:stack.Size()]
		stack.Element = nil
	case stack.Size()== 0 :
		stack.Element = other.Element
		other.Element = nil
	default:
		stack.Element,other.Element = other.Element,stack.Element
	}
	return
}

//修改指定索引的值
func (stack *Stack)Set(idx int,value interface{})(err error){
	if idx >= 0 && stack.Size() > 0 && stack.Size() > idx{
		stack.Element[idx] = value
		return nil
	}
	return errors.New("Set失败!")
}

//Stack的size
func (stack *Stack)Size()(int){
	return len(stack.Element)
}

//是否为空
func (stack *Stack)Empty()(bool){
	if stack.Element == nil || stack.Size() == 0 {
		return true
	}
	return false
}

//打印
func (stack *Stack)Print(){
	for i := len(stack.Element) - 1; i >= 0; i--{
		fmt.Println(i,"=>",stack.Element[i])
	}
}
<|endoftext|>


<commit_before>package speaker

import (
	"math"
	"sync"
	"time"

	"github.com/faiface/pixel/audio"
	"github.com/hajimehoshi/oto"
	"github.com/pkg/errors"
)

var (
	mu      sync.Mutex
	mixer   audio.Mixer
	samples [][2]float64
	buf     []byte
	player  *oto.Player
	done    chan struct{}
)

// Init initializes audio playback through speaker. Must be called before using this package. The
// value of audio.SampleRate must be set (or left to the default) before calling this function.
//
// The bufferSize argument specifies the length of the speaker's buffer. Bigger bufferSize means
// lower CPU usage and more reliable playback. Lower bufferSize means better responsiveness and less
// delay.
func Init(bufferSize time.Duration) error {
	mu.Lock()
	defer mu.Unlock()

	if player != nil {
		done <- struct{}{}
		player.Close()
	}

	mixer = audio.Mixer{}

	numSamples := int(math.Ceil(bufferSize.Seconds() * audio.SampleRate))
	numBytes := numSamples * 4

	samples = make([][2]float64, numSamples)
	buf = make([]byte, numBytes)

	var err error
	player, err = oto.NewPlayer(int(audio.SampleRate), 2, 2, numBytes)
	if err != nil {
		return errors.Wrap(err, "failed to initialize speaker")
	}

	done = make(chan struct{})

	go func() {
		for {
			select {
			default:
				update()
			case <-done:
				return
			}
		}
	}()

	return nil
}

// Lock locks the speaker. While locked, speaker won't pull new data from the playing Stramers. Lock
// if you want to modify any currently playing Streamers to avoid race conditions.
func Lock() {
	mu.Lock()
}

// Unlock unlocks the speaker. Call after modifying any currently playing Streamer.
func Unlock() {
	mu.Unlock()
}

// Play starts playing all provided Streamers through the speaker.
func Play(s ...audio.Streamer) {
	mu.Lock()
	mixer.Play(s...)
	mu.Unlock()
}

// update pulls new data from the playing Streamers and sends it to the speaker. Blocks until the
// data is sent and started playing.
func update() {
	mu.Lock()
	mixer.Stream(samples)
	mu.Unlock()

	for i := range samples {
		for c := range samples[i] {
			val := samples[i][c]
			if val < -1 {
				val = -1
			}
			if val > +1 {
				val = +1
			}
			valInt16 := int16(val * (1<<15 - 1))
			low := byte(valInt16 % (1 << 8))
			high := byte(valInt16 / (1 << 8))
			buf[i*4+c*2+0] = low
			buf[i*4+c*2+1] = high
		}
	}

	player.Write(buf)
}
<commit_msg>audio: speaker: fix byte encoding<commit_after>package speaker

import (
	"math"
	"sync"
	"time"

	"github.com/faiface/pixel/audio"
	"github.com/hajimehoshi/oto"
	"github.com/pkg/errors"
)

var (
	mu      sync.Mutex
	mixer   audio.Mixer
	samples [][2]float64
	buf     []byte
	player  *oto.Player
	done    chan struct{}
)

// Init initializes audio playback through speaker. Must be called before using this package. The
// value of audio.SampleRate must be set (or left to the default) before calling this function.
//
// The bufferSize argument specifies the length of the speaker's buffer. Bigger bufferSize means
// lower CPU usage and more reliable playback. Lower bufferSize means better responsiveness and less
// delay.
func Init(bufferSize time.Duration) error {
	mu.Lock()
	defer mu.Unlock()

	if player != nil {
		done <- struct{}{}
		player.Close()
	}

	mixer = audio.Mixer{}

	numSamples := int(math.Ceil(bufferSize.Seconds() * audio.SampleRate))
	numBytes := numSamples * 4

	samples = make([][2]float64, numSamples)
	buf = make([]byte, numBytes)

	var err error
	player, err = oto.NewPlayer(int(audio.SampleRate), 2, 2, numBytes)
	if err != nil {
		return errors.Wrap(err, "failed to initialize speaker")
	}

	done = make(chan struct{})

	go func() {
		for {
			select {
			default:
				update()
			case <-done:
				return
			}
		}
	}()

	return nil
}

// Lock locks the speaker. While locked, speaker won't pull new data from the playing Stramers. Lock
// if you want to modify any currently playing Streamers to avoid race conditions.
func Lock() {
	mu.Lock()
}

// Unlock unlocks the speaker. Call after modifying any currently playing Streamer.
func Unlock() {
	mu.Unlock()
}

// Play starts playing all provided Streamers through the speaker.
func Play(s ...audio.Streamer) {
	mu.Lock()
	mixer.Play(s...)
	mu.Unlock()
}

// update pulls new data from the playing Streamers and sends it to the speaker. Blocks until the
// data is sent and started playing.
func update() {
	mu.Lock()
	mixer.Stream(samples)
	mu.Unlock()

	for i := range samples {
		for c := range samples[i] {
			val := samples[i][c]
			if val < -1 {
				val = -1
			}
			if val > +1 {
				val = +1
			}
			valInt16 := int16(val * (1<<15 - 1))
			low := byte(valInt16)
			high := byte(valInt16 >> 8)
			buf[i*4+c*2+0] = low
			buf[i*4+c*2+1] = high
		}
	}

	player.Write(buf)
}
<|endoftext|>


<commit_before>package gopdf

import (
	"bytes"
	"compress/zlib"
	"encoding/binary"
	"errors"
	"fmt"
	"image"
	"image/color"
	"io/ioutil"
	"strings"
)

func buildImgProp(imginfo imgInfo) (*bytes.Buffer, error) {

	var buffer bytes.Buffer
	buffer.WriteString("<</Type /XObject\n")
	buffer.WriteString("/Subtype /Image\n")
	buffer.WriteString(fmt.Sprintf("/Width %d\n", imginfo.w))  // /Width 675\n"
	buffer.WriteString(fmt.Sprintf("/Height %d\n", imginfo.h)) //  /Height 942\n"
	if isColspaceIndexed(imginfo) {
		size := len(imginfo.pal)/3 - 1
		buffer.WriteString(fmt.Sprintf("/ColorSpace [/Indexed /DeviceRGB %d %d 0 R]", size, imginfo.deviceRGBObjID+1))
	} else {
		buffer.WriteString(fmt.Sprintf("/ColorSpace /%s\n", imginfo.colspace))
		if imginfo.colspace == "DeviceCMYK" {
			buffer.WriteString("/Decode [1 0 1 0 1 0 1 0]\n")
		}
	}
	buffer.WriteString(fmt.Sprintf("/BitsPerComponent %s\n", imginfo.bitsPerComponent))
	if strings.TrimSpace(imginfo.filter) != "" {
		buffer.WriteString(fmt.Sprintf("/Filter /%s\n", imginfo.filter))
	}

	if strings.TrimSpace(imginfo.decodeParms) != "" {
		buffer.WriteString(fmt.Sprintf("/DecodeParms <<%s>>\n", imginfo.decodeParms))
	}

	if imginfo.trns != nil && len(imginfo.trns) > 0 {
		j := 0
		max := len(imginfo.trns)
		var trns bytes.Buffer
		for j < max {
			trns.WriteByte(imginfo.trns[j])
			trns.WriteString(" ")
			trns.WriteByte(imginfo.trns[j])
			trns.WriteString(" ")
			j++
		}
		buffer.WriteString(fmt.Sprintf("/Mask [%s]\n", trns.String()))
	}

	if haveSMask(imginfo) {
		buffer.WriteString(fmt.Sprintf("/SMask %d 0 R\n", imginfo.smarkObjID+1))
	}

	return &buffer, nil
}

func isColspaceIndexed(imginfo imgInfo) bool {
	if imginfo.colspace == "Indexed" {
		return true
	}
	return false
}

func haveSMask(imginfo imgInfo) bool {
	if imginfo.smask != nil && len(imginfo.smask) > 0 {
		return true
	}
	return false
}

func parseImg(raw []byte) (imgInfo, error) {
	//fmt.Printf("----------\n")
	var info imgInfo
	imgConfig, formatname, err := image.DecodeConfig(bytes.NewBuffer(raw))
	if err != nil {
		return info, err
	}
	info.formatName = formatname

	if formatname == "jpeg" {

		err = parseImgJpg(&info, imgConfig)
		if err != nil {
			return info, err
		}
		info.data = raw
	} else if formatname == "png" {
		err = paesePng(raw, &info, imgConfig)
		if err != nil {
			return info, err
		}
	}

	//fmt.Printf("%#v\n", info)

	return info, nil
}

func parseImgJpg(info *imgInfo, imgConfig image.Config) error {
	if imgConfig.ColorModel == color.YCbCrModel {
		info.colspace = "DeviceRGB"
	} else if imgConfig.ColorModel == color.GrayModel {
		info.colspace = "DeviceGray"
	} else if imgConfig.ColorModel == color.CMYKModel {
		info.colspace = "DeviceCMYK"
	} else {
		return errors.New("color model not support")
	}
	info.bitsPerComponent = "8"
	info.filter = "DCTDecode"

	info.h = imgConfig.Height
	info.w = imgConfig.Width

	return nil
}

var pngMagicNumber = []byte{0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a}
var pngIHDR = []byte{0x49, 0x48, 0x44, 0x52}

func paesePng(raw []byte, info *imgInfo, imgConfig image.Config) error {
	f := bytes.NewReader(raw)
	f.Seek(0, 0)
	b, err := readBytes(f, 8)
	if err != nil {
		return err
	}
	if !compareBytes(b, pngMagicNumber) {
		return errors.New("Not a PNG file")
	}

	f.Seek(4, 1) //skip header chunk
	b, err = readBytes(f, 4)
	if err != nil {
		return err
	}
	if !compareBytes(b, pngIHDR) {
		return errors.New("Incorrect PNG file")
	}

	w, err := readInt(f)
	if err != nil {
		return err
	}
	h, err := readInt(f)
	if err != nil {
		return err
	}
	//fmt.Printf("w=%d h=%d\n", w, h)

	bpc, err := readBytes(f, 1)
	if err != nil {
		return err
	}

	if bpc[0] > 8 {
		return errors.New("16-bit depth not supported")
	}

	ct, err := readBytes(f, 1)
	if err != nil {
		return err
	}

	var colspace string
	if ct[0] == 0 || ct[0] == 4 {
		colspace = "DeviceGray"
	} else if ct[0] == 2 || ct[0] == 6 {
		colspace = "DeviceRGB"
	} else if ct[0] == 3 {
		colspace = "Indexed"
	} else {
		return errors.New("Unknown color type")
	}

	compressionMethod, err := readBytes(f, 1)
	if err != nil {
		return err
	}
	if compressionMethod[0] != 0 {
		return errors.New("Unknown compression method")
	}

	filterMethod, err := readBytes(f, 1)
	if err != nil {
		return err
	}
	if filterMethod[0] != 0 {
		return errors.New("Unknown filter method")
	}

	interlacing, err := readBytes(f, 1)
	if err != nil {
		return err
	}
	if interlacing[0] != 0 {
		return errors.New("Interlacing not supported")
	}

	_, err = f.Seek(4, 1) //skip
	if err != nil {
		return err
	}

	//decodeParms := "/Predictor 15 /Colors '.($colspace=='DeviceRGB' ? 3 : 1).' /BitsPerComponent '.$bpc.' /Columns '.$w;

	var pal []byte
	var trns []byte
	var data []byte
	for {
		n, err := readInt(f)
		if err != nil {
			return err
		}

		typ, err := readBytes(f, 4)
		//fmt.Printf(">>>>>%s\n", string(typ))
		if err != nil {
			return err
		}

		if string(typ) == "PLTE" {
			pal, err = readBytes(f, n)
			if err != nil {
				return err
			}
			_, err = f.Seek(int64(4), 1) //skip
			if err != nil {
				return err
			}
		} else if string(typ) == "tRNS" {

			var t []byte
			t, err = readBytes(f, n)
			if err != nil {
				return err
			}

			if ct[0] == 0 {
				trns = []byte{(t[1])}
			} else if ct[0] == 2 {
				trns = []byte{t[1], t[3], t[5]}
			} else {
				pos := strings.Index(string(t), "\x00")
				if pos >= 0 {
					trns = []byte{byte(pos)}
				}
			}

			_, err = f.Seek(int64(4), 1) //skip
			if err != nil {
				return err
			}

		} else if string(typ) == "IDAT" {
			//fmt.Printf("n=%d\n\n", n)
			var d []byte
			d, err = readBytes(f, n)
			if err != nil {
				return err
			}
			data = append(data, d...)
			_, err = f.Seek(int64(4), 1) //skip
			if err != nil {
				return err
			}
		} else if string(typ) == "IEND" {
			break
		} else {
			_, err = f.Seek(int64(n+4), 1) //skip
			if err != nil {
				return err
			}
		}

		if n <= 0 {
			break
		}
	} //end for

	//info.data = data //ok
	info.trns = trns
	info.pal = pal

	//fmt.Printf("data= %x", md5.Sum(data))

	if colspace == "Indexed" && strings.TrimSpace(string(pal)) == "" {
		return errors.New("Missing palette")
	}

	info.w = w
	info.h = h
	info.colspace = colspace
	info.bitsPerComponent = fmt.Sprintf("%d", int(bpc[0]))
	info.filter = "FlateDecode"

	colors := 1
	if colspace == "DeviceRGB" {
		colors = 3
	}
	info.decodeParms = fmt.Sprintf("/Predictor 15 /Colors  %d /BitsPerComponent %s /Columns %d", colors, info.bitsPerComponent, w)

	//fmt.Printf("%d = ct[0]\n", ct[0])
	//fmt.Printf("%x\n", md5.Sum(data))
	if ct[0] >= 4 {
		zipReader, err := zlib.NewReader(bytes.NewReader(data))
		if err != nil {
			return err
		}
		defer zipReader.Close()
		afterZipData, err := ioutil.ReadAll(zipReader)
		if err != nil {
			return err
		}

		var color []byte
		var alpha []byte
		if ct[0] == 4 {
			// Gray image
			length := 2 * w
			i := 0
			for i < h {
				pos := (1 + length) * i
				color = append(color, afterZipData[pos])
				alpha = append(alpha, afterZipData[pos])
				line := afterZipData[pos+1 : pos+length+1]
				j := 0
				max := len(line)
				for j < max {
					color = append(color, line[j])
					j++
					alpha = append(alpha, line[j])
					j++
				}
				i++
			}
			//fmt.Print("aaaaa")

		} else {
			// RGB image
			length := 4 * w
			i := 0
			for i < h {
				pos := (1 + length) * i
				color = append(color, afterZipData[pos])
				alpha = append(alpha, afterZipData[pos])
				line := afterZipData[pos+1 : pos+length+1]
				j := 0
				max := len(line)
				for j < max {
					color = append(color, line[j:j+3]...)
					alpha = append(alpha, line[j+3])
					j = j + 4
				}

				i++
			}
			info.smask, err = compress(alpha)
			if err != nil {
				return err
			}

			info.data, err = compress(color)
			if err != nil {
				return err
			}
		}

	} else {
		info.data = data
	}

	return nil
}

func compress(data []byte) ([]byte, error) {
	var results []byte
	var buff bytes.Buffer
	zwr, err := zlib.NewWriterLevel(&buff, zlib.BestSpeed)
	if err != nil {
		return results, err
	}
	_, err = zwr.Write(data)
	if err != nil {
		return results, err
	}
	zwr.Close()
	return buff.Bytes(), nil
}

func readUInt(f *bytes.Reader) (uint, error) {
	buff, err := readBytes(f, 4)
	//fmt.Printf("%#v\n\n", buff)
	if err != nil {
		return 0, err
	}
	n := binary.BigEndian.Uint32(buff)
	return uint(n), nil
}

func readInt(f *bytes.Reader) (int, error) {

	u, err := readUInt(f)
	if err != nil {
		return 0, err
	}
	var v int
	if u >= 0x8000 {
		v = int(u) - 65536
	} else {
		v = int(u)
	}
	return v, nil
}

func readBytes(f *bytes.Reader, len int) ([]byte, error) {
	b := make([]byte, len)
	_, err := f.Read(b)
	if err != nil {
		return nil, err
	}
	return b, nil
}

func compareBytes(a []byte, b []byte) bool {
	if a == nil && b == nil {
		return true
	} else if a == nil {
		return false
	} else if b == nil {
		return false
	}

	if len(a) != len(b) {
		return false
	}

	i := 0
	max := len(a)
	for i < max {
		if a[i] != b[i] {
			return false
		}
		i++
	}
	return true
}

func isDeviceRGB(formatname string, img *image.Image) bool {
	if _, ok := (*img).(*image.YCbCr); ok {
		return true
	} else if _, ok := (*img).(*image.NRGBA); ok {
		return true
	}
	return false
}

//ImgReactagleToWH  Rectangle to W and H
func ImgReactagleToWH(imageRect image.Rectangle) (float64, float64) {
	k := 1
	w := -128 //init
	h := -128 //init
	if w < 0 {
		w = -imageRect.Dx() * 72 / w / k
	}
	if h < 0 {
		h = -imageRect.Dy() * 72 / h / k
	}
	if w == 0 {
		w = h * imageRect.Dx() / imageRect.Dy()
	}
	if h == 0 {
		h = w * imageRect.Dy() / imageRect.Dx()
	}
	return float64(w), float64(h)
}
<commit_msg>fixed #28<commit_after>package gopdf

import (
	"bytes"
	"compress/zlib"
	"encoding/binary"
	"errors"
	"fmt"
	"image"
	"image/color"
	"io/ioutil"
	"strings"
)

func buildImgProp(imginfo imgInfo) (*bytes.Buffer, error) {

	var buffer bytes.Buffer
	buffer.WriteString("<</Type /XObject\n")
	buffer.WriteString("/Subtype /Image\n")
	buffer.WriteString(fmt.Sprintf("/Width %d\n", imginfo.w))  // /Width 675\n"
	buffer.WriteString(fmt.Sprintf("/Height %d\n", imginfo.h)) //  /Height 942\n"
	if isColspaceIndexed(imginfo) {
		size := len(imginfo.pal)/3 - 1
		buffer.WriteString(fmt.Sprintf("/ColorSpace [/Indexed /DeviceRGB %d %d 0 R]\n", size, imginfo.deviceRGBObjID+1))
	} else {
		buffer.WriteString(fmt.Sprintf("/ColorSpace /%s\n", imginfo.colspace))
		if imginfo.colspace == "DeviceCMYK" {
			buffer.WriteString("/Decode [1 0 1 0 1 0 1 0]\n")
		}
	}
	buffer.WriteString(fmt.Sprintf("/BitsPerComponent %s\n", imginfo.bitsPerComponent))
	if strings.TrimSpace(imginfo.filter) != "" {
		buffer.WriteString(fmt.Sprintf("/Filter /%s\n", imginfo.filter))
	}

	if strings.TrimSpace(imginfo.decodeParms) != "" {
		buffer.WriteString(fmt.Sprintf("/DecodeParms <<%s>>\n", imginfo.decodeParms))
	}

	if imginfo.trns != nil && len(imginfo.trns) > 0 {
		j := 0
		max := len(imginfo.trns)
		var trns bytes.Buffer
		for j < max {
			//trn := int(imginfo.trns[j])
			//trns.WriteByte(imginfo.trns[j])
			trns.WriteString(fmt.Sprintf("%d", imginfo.trns[j]))
			trns.WriteString(" ")
			trns.WriteString(fmt.Sprintf("%d", imginfo.trns[j]))
			trns.WriteString(" ")
			j++
		}
		buffer.WriteString(fmt.Sprintf("/Mask [%s]\n", trns.String()))
	}

	if haveSMask(imginfo) {
		buffer.WriteString(fmt.Sprintf("/SMask %d 0 R\n", imginfo.smarkObjID+1))
	}

	return &buffer, nil
}

func isColspaceIndexed(imginfo imgInfo) bool {
	if imginfo.colspace == "Indexed" {
		return true
	}
	return false
}

func haveSMask(imginfo imgInfo) bool {
	if imginfo.smask != nil && len(imginfo.smask) > 0 {
		return true
	}
	return false
}

func parseImg(raw []byte) (imgInfo, error) {
	//fmt.Printf("----------\n")
	var info imgInfo
	imgConfig, formatname, err := image.DecodeConfig(bytes.NewBuffer(raw))
	if err != nil {
		return info, err
	}
	info.formatName = formatname

	if formatname == "jpeg" {

		err = parseImgJpg(&info, imgConfig)
		if err != nil {
			return info, err
		}
		info.data = raw
	} else if formatname == "png" {
		err = paesePng(raw, &info, imgConfig)
		if err != nil {
			return info, err
		}
	}

	//fmt.Printf("%#v\n", info)

	return info, nil
}

func parseImgJpg(info *imgInfo, imgConfig image.Config) error {
	if imgConfig.ColorModel == color.YCbCrModel {
		info.colspace = "DeviceRGB"
	} else if imgConfig.ColorModel == color.GrayModel {
		info.colspace = "DeviceGray"
	} else if imgConfig.ColorModel == color.CMYKModel {
		info.colspace = "DeviceCMYK"
	} else {
		return errors.New("color model not support")
	}
	info.bitsPerComponent = "8"
	info.filter = "DCTDecode"

	info.h = imgConfig.Height
	info.w = imgConfig.Width

	return nil
}

var pngMagicNumber = []byte{0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a}
var pngIHDR = []byte{0x49, 0x48, 0x44, 0x52}

func paesePng(raw []byte, info *imgInfo, imgConfig image.Config) error {
	f := bytes.NewReader(raw)
	f.Seek(0, 0)
	b, err := readBytes(f, 8)
	if err != nil {
		return err
	}
	if !compareBytes(b, pngMagicNumber) {
		return errors.New("Not a PNG file")
	}

	f.Seek(4, 1) //skip header chunk
	b, err = readBytes(f, 4)
	if err != nil {
		return err
	}
	if !compareBytes(b, pngIHDR) {
		return errors.New("Incorrect PNG file")
	}

	w, err := readInt(f)
	if err != nil {
		return err
	}
	h, err := readInt(f)
	if err != nil {
		return err
	}
	//fmt.Printf("w=%d h=%d\n", w, h)

	bpc, err := readBytes(f, 1)
	if err != nil {
		return err
	}

	if bpc[0] > 8 {
		return errors.New("16-bit depth not supported")
	}

	ct, err := readBytes(f, 1)
	if err != nil {
		return err
	}

	var colspace string
	if ct[0] == 0 || ct[0] == 4 {
		colspace = "DeviceGray"
	} else if ct[0] == 2 || ct[0] == 6 {
		colspace = "DeviceRGB"
	} else if ct[0] == 3 {
		colspace = "Indexed"
	} else {
		return errors.New("Unknown color type")
	}

	compressionMethod, err := readBytes(f, 1)
	if err != nil {
		return err
	}
	if compressionMethod[0] != 0 {
		return errors.New("Unknown compression method")
	}

	filterMethod, err := readBytes(f, 1)
	if err != nil {
		return err
	}
	if filterMethod[0] != 0 {
		return errors.New("Unknown filter method")
	}

	interlacing, err := readBytes(f, 1)
	if err != nil {
		return err
	}
	if interlacing[0] != 0 {
		return errors.New("Interlacing not supported")
	}

	_, err = f.Seek(4, 1) //skip
	if err != nil {
		return err
	}

	//decodeParms := "/Predictor 15 /Colors '.($colspace=='DeviceRGB' ? 3 : 1).' /BitsPerComponent '.$bpc.' /Columns '.$w;

	var pal []byte
	var trns []byte
	var data []byte
	for {
		n, err := readInt(f)
		if err != nil {
			return err
		}

		typ, err := readBytes(f, 4)
		//fmt.Printf(">>>>>%s\n", string(typ))
		if err != nil {
			return err
		}

		if string(typ) == "PLTE" {
			pal, err = readBytes(f, n)
			if err != nil {
				return err
			}
			_, err = f.Seek(int64(4), 1) //skip
			if err != nil {
				return err
			}
		} else if string(typ) == "tRNS" {

			var t []byte
			t, err = readBytes(f, n)
			if err != nil {
				return err
			}

			if ct[0] == 0 {
				trns = []byte{(t[1])}
			} else if ct[0] == 2 {
				trns = []byte{t[1], t[3], t[5]}
			} else {
				pos := strings.Index(string(t), "\x00")
				if pos >= 0 {
					trns = []byte{byte(pos)}
				}
			}

			_, err = f.Seek(int64(4), 1) //skip
			if err != nil {
				return err
			}

		} else if string(typ) == "IDAT" {
			//fmt.Printf("n=%d\n\n", n)
			var d []byte
			d, err = readBytes(f, n)
			if err != nil {
				return err
			}
			data = append(data, d...)
			_, err = f.Seek(int64(4), 1) //skip
			if err != nil {
				return err
			}
		} else if string(typ) == "IEND" {
			break
		} else {
			_, err = f.Seek(int64(n+4), 1) //skip
			if err != nil {
				return err
			}
		}

		if n <= 0 {
			break
		}
	} //end for

	//info.data = data //ok
	info.trns = trns
	info.pal = pal

	//fmt.Printf("data= %x", md5.Sum(data))

	if colspace == "Indexed" && strings.TrimSpace(string(pal)) == "" {
		return errors.New("Missing palette")
	}

	info.w = w
	info.h = h
	info.colspace = colspace
	info.bitsPerComponent = fmt.Sprintf("%d", int(bpc[0]))
	info.filter = "FlateDecode"

	colors := 1
	if colspace == "DeviceRGB" {
		colors = 3
	}
	info.decodeParms = fmt.Sprintf("/Predictor 15 /Colors  %d /BitsPerComponent %s /Columns %d", colors, info.bitsPerComponent, w)

	//fmt.Printf("%d = ct[0]\n", ct[0])
	//fmt.Printf("%x\n", md5.Sum(data))
	if ct[0] >= 4 {
		zipReader, err := zlib.NewReader(bytes.NewReader(data))
		if err != nil {
			return err
		}
		defer zipReader.Close()
		afterZipData, err := ioutil.ReadAll(zipReader)
		if err != nil {
			return err
		}

		var color []byte
		var alpha []byte
		if ct[0] == 4 {
			// Gray image
			length := 2 * w
			i := 0
			for i < h {
				pos := (1 + length) * i
				color = append(color, afterZipData[pos])
				alpha = append(alpha, afterZipData[pos])
				line := afterZipData[pos+1 : pos+length+1]
				j := 0
				max := len(line)
				for j < max {
					color = append(color, line[j])
					j++
					alpha = append(alpha, line[j])
					j++
				}
				i++
			}
			//fmt.Print("aaaaa")

		} else {
			// RGB image
			length := 4 * w
			i := 0
			for i < h {
				pos := (1 + length) * i
				color = append(color, afterZipData[pos])
				alpha = append(alpha, afterZipData[pos])
				line := afterZipData[pos+1 : pos+length+1]
				j := 0
				max := len(line)
				for j < max {
					color = append(color, line[j:j+3]...)
					alpha = append(alpha, line[j+3])
					j = j + 4
				}

				i++
			}
			info.smask, err = compress(alpha)
			if err != nil {
				return err
			}

			info.data, err = compress(color)
			if err != nil {
				return err
			}
		}

	} else {
		info.data = data
	}

	return nil
}

func compress(data []byte) ([]byte, error) {
	var results []byte
	var buff bytes.Buffer
	zwr, err := zlib.NewWriterLevel(&buff, zlib.BestSpeed)
	if err != nil {
		return results, err
	}
	_, err = zwr.Write(data)
	if err != nil {
		return results, err
	}
	zwr.Close()
	return buff.Bytes(), nil
}

func readUInt(f *bytes.Reader) (uint, error) {
	buff, err := readBytes(f, 4)
	//fmt.Printf("%#v\n\n", buff)
	if err != nil {
		return 0, err
	}
	n := binary.BigEndian.Uint32(buff)
	return uint(n), nil
}

func readInt(f *bytes.Reader) (int, error) {

	u, err := readUInt(f)
	if err != nil {
		return 0, err
	}
	var v int
	if u >= 0x8000 {
		v = int(u) - 65536
	} else {
		v = int(u)
	}
	return v, nil
}

func readBytes(f *bytes.Reader, len int) ([]byte, error) {
	b := make([]byte, len)
	_, err := f.Read(b)
	if err != nil {
		return nil, err
	}
	return b, nil
}

func compareBytes(a []byte, b []byte) bool {
	if a == nil && b == nil {
		return true
	} else if a == nil {
		return false
	} else if b == nil {
		return false
	}

	if len(a) != len(b) {
		return false
	}

	i := 0
	max := len(a)
	for i < max {
		if a[i] != b[i] {
			return false
		}
		i++
	}
	return true
}

func isDeviceRGB(formatname string, img *image.Image) bool {
	if _, ok := (*img).(*image.YCbCr); ok {
		return true
	} else if _, ok := (*img).(*image.NRGBA); ok {
		return true
	}
	return false
}

//ImgReactagleToWH  Rectangle to W and H
func ImgReactagleToWH(imageRect image.Rectangle) (float64, float64) {
	k := 1
	w := -128 //init
	h := -128 //init
	if w < 0 {
		w = -imageRect.Dx() * 72 / w / k
	}
	if h < 0 {
		h = -imageRect.Dy() * 72 / h / k
	}
	if w == 0 {
		w = h * imageRect.Dx() / imageRect.Dy()
	}
	if h == 0 {
		h = w * imageRect.Dy() / imageRect.Dx()
	}
	return float64(w), float64(h)
}
<|endoftext|>


<commit_before>package node

import "github.com/philharnish/forge/src/data/graph/bloom/weight"

type NodeMetadata = []*weight.WeightedString

type NodeMetadataProvider interface {
	Metadata(paths []string, items []NodeItems) NodeMetadata
}
<commit_msg>Add NodeMetadata type alias.<commit_after>package node

import (
	"github.com/philharnish/forge/src/data/graph/bloom/weight"
)

type NodeMetadata = []*weight.WeightedString
<|endoftext|>


<commit_before>package main

import (
	"github.com/emicklei/go-restful"
	"github.com/emicklei/go-restful/swagger"
	"google.golang.com/appengine"
	"google.golang.com/appengine/datastore"
	"google.golang.com/appengine/user"
	"net/http"
	"time"
)

// This example demonstrates a reasonably complete suite of RESTful operations backed
// by DataStore on Google App Engine.

// Our simple example struct.
type Profile struct {
	LastModified time.Time `json:"-" xml:"-"`
	Email        string    `json:"-" xml:"-"`
	FirstName    string    `json:"first_name" xml:"first-name"`
	NickName     string    `json:"nick_name" xml:"nick-name"`
	LastName     string    `json:"last_name" xml:"last-name"`
}

type ProfileApi struct {
	Path string
}

func gaeUrl() string {
	if appengine.IsDevAppServer() {
		return "http://localhost:8080"
	} else {
		// Include your URL on App Engine here.
		// I found no way to get AppID without appengine.Context and this always
		// based on a http.Request.
		return "http://federatedservices.appspot.com"
	}
}

func init() {
	u := ProfileApi{Path: "/profiles"}
	u.register()

	// Optionally, you can install the Swagger Service which provides a nice Web UI on your REST API
	// You need to download the Swagger HTML5 assets and change the FilePath location in the config below.
	// Open <your_app_id>.appspot.com/apidocs and enter
	// Place the Swagger UI files into a folder called static/swagger if you wish to use Swagger
	// http://<your_app_id>.appspot.com/apidocs.json in the api input field.
	// For testing, you can use http://localhost:8080/apidocs.json
	config := swagger.Config{
		// You control what services are visible
		WebServices:    restful.RegisteredWebServices(),
		WebServicesUrl: gaeUrl(),
		ApiPath:        "/apidocs.json",

		// Optionally, specifiy where the UI is located
		SwaggerPath: "/apidocs/",

		// GAE support static content which is configured in your app.yaml.
		// This example expect the swagger-ui in static/swagger so you should place it there :)
		SwaggerFilePath: "static/swagger"}
	swagger.InstallSwaggerService(config)
}

func (u ProfileApi) register() {
	ws := new(restful.WebService)

	ws.
		Path(u.Path).
		// You can specify consumes and produces per route as well.
		Consumes(restful.MIME_JSON, restful.MIME_XML).
		Produces(restful.MIME_JSON, restful.MIME_XML)

	ws.Route(ws.POST("").To(u.insert).
		// Swagger documentation.
		Doc("insert a new profile").
		Param(ws.BodyParameter("Profile", "representation of a profile").DataType("main.Profile")).
		Reads(Profile{}))

	ws.Route(ws.GET("/{profile-id}").To(u.read).
		// Swagger documentation.
		Doc("read a profile").
		Param(ws.PathParameter("profile-id", "identifier for a profile").DataType("string")).
		Writes(Profile{}))

	ws.Route(ws.PUT("/{profile-id}").To(u.update).
		// Swagger documentation.
		Doc("update an existing profile").
		Param(ws.PathParameter("profile-id", "identifier for a profile").DataType("string")).
		Param(ws.BodyParameter("Profile", "representation of a profile").DataType("main.Profile")).
		Reads(Profile{}))

	ws.Route(ws.DELETE("/{profile-id}").To(u.remove).
		// Swagger documentation.
		Doc("remove a profile").
		Param(ws.PathParameter("profile-id", "identifier for a profile").DataType("string")))

	restful.Add(ws)
}

// POST http://localhost:8080/profiles
// {"first_name": "Ivan", "nick_name": "Socks", "last_name": "Hawkes"}
//
func (u *ProfileApi) insert(r *restful.Request, w *restful.Response) {
	c := appengine.NewContext(r.Request)

	// Marshall the entity from the request into a struct.
	p := new(Profile)
	err := r.ReadEntity(&p)
	if err != nil {
		w.WriteError(http.StatusNotAcceptable, err)
		return
	}

	// Ensure we start with a sensible value for this field.
	p.LastModified = time.Now()

	// The profile belongs to this user.
	p.Email = user.Current(c).String()

	k, err := datastore.Put(c, datastore.NewIncompleteKey(c, "profiles", nil), p)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// Let them know the location of the newly created resource.
	// TODO: Use a safe Url path append function.
	w.AddHeader("Location", u.Path+"/"+k.Encode())

	// Return the resultant entity.
	w.WriteHeader(http.StatusCreated)
	w.WriteEntity(p)
}

// GET http://localhost:8080/profiles/ahdkZXZ-ZmVkZXJhdGlvbi1zZXJ2aWNlc3IVCxIIcHJvZmlsZXMYgICAgICAgAoM
//
func (u ProfileApi) read(r *restful.Request, w *restful.Response) {
	c := appengine.NewContext(r.Request)

	// Decode the request parameter to determine the key for the entity.
	k, err := datastore.DecodeKey(r.PathParameter("profile-id"))
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	// Retrieve the entity from the datastore.
	p := Profile{}
	if err := datastore.Get(c, k, &p); err != nil {
		if err.Error() == "datastore: no such entity" {
			http.Error(w, err.Error(), http.StatusNotFound)
		} else {
			http.Error(w, err.Error(), http.StatusInternalServerError)
		}
		return
	}

	// Check we own the profile before allowing them to view it.
	// Optionally, return a 404 instead to help prevent guessing ids.
	// TODO: Allow admins access.
	if p.Email != user.Current(c).String() {
		http.Error(w, "You do not have access to this resource", http.StatusForbidden)
		return
	}

	w.WriteEntity(p)
}

// PUT http://localhost:8080/profiles/ahdkZXZ-ZmVkZXJhdGlvbi1zZXJ2aWNlc3IVCxIIcHJvZmlsZXMYgICAgICAgAoM
// {"first_name": "Ivan", "nick_name": "Socks", "last_name": "Hawkes"}
//
func (u *ProfileApi) update(r *restful.Request, w *restful.Response) {
	c := appengine.NewContext(r.Request)

	// Decode the request parameter to determine the key for the entity.
	k, err := datastore.DecodeKey(r.PathParameter("profile-id"))
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	// Marshall the entity from the request into a struct.
	p := new(Profile)
	err = r.ReadEntity(&p)
	if err != nil {
		w.WriteError(http.StatusNotAcceptable, err)
		return
	}

	// Retrieve the old entity from the datastore.
	old := Profile{}
	if err := datastore.Get(c, k, &old); err != nil {
		if err.Error() == "datastore: no such entity" {
			http.Error(w, err.Error(), http.StatusNotFound)
		} else {
			http.Error(w, err.Error(), http.StatusInternalServerError)
		}
		return
	}

	// Check we own the profile before allowing them to update it.
	// Optionally, return a 404 instead to help prevent guessing ids.
	// TODO: Allow admins access.
	if old.Email != user.Current(c).String() {
		http.Error(w, "You do not have access to this resource", http.StatusForbidden)
		return
	}

	// Since the whole entity is re-written, we need to assign any invariant fields again
	// e.g. the owner of the entity.
	p.Email = user.Current(c).String()

	// Keep track of the last modification date.
	p.LastModified = time.Now()

	// Attempt to overwrite the old entity.
	_, err = datastore.Put(c, k, p)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// Let them know it succeeded.
	w.WriteHeader(http.StatusNoContent)
}

// DELETE http://localhost:8080/profiles/ahdkZXZ-ZmVkZXJhdGlvbi1zZXJ2aWNlc3IVCxIIcHJvZmlsZXMYgICAgICAgAoM
//
func (u *ProfileApi) remove(r *restful.Request, w *restful.Response) {
	c := appengine.NewContext(r.Request)

	// Decode the request parameter to determine the key for the entity.
	k, err := datastore.DecodeKey(r.PathParameter("profile-id"))
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	// Retrieve the old entity from the datastore.
	old := Profile{}
	if err := datastore.Get(c, k, &old); err != nil {
		if err.Error() == "datastore: no such entity" {
			http.Error(w, err.Error(), http.StatusNotFound)
		} else {
			http.Error(w, err.Error(), http.StatusInternalServerError)
		}
		return
	}

	// Check we own the profile before allowing them to delete it.
	// Optionally, return a 404 instead to help prevent guessing ids.
	// TODO: Allow admins access.
	if old.Email != user.Current(c).String() {
		http.Error(w, "You do not have access to this resource", http.StatusForbidden)
		return
	}

	// Delete the entity.
	if err := datastore.Delete(c, k); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}

	// Success notification.
	w.WriteHeader(http.StatusNoContent)
}
<commit_msg>fix example imports<commit_after>package main

import (
	"github.com/emicklei/go-restful"
	"github.com/emicklei/go-restful/swagger"
	"google.golang.org/appengine"
	"google.golang.org/appengine/datastore"
	"google.golang.org/appengine/user"
	"net/http"
	"time"
)

// This example demonstrates a reasonably complete suite of RESTful operations backed
// by DataStore on Google App Engine.

// Our simple example struct.
type Profile struct {
	LastModified time.Time `json:"-" xml:"-"`
	Email        string    `json:"-" xml:"-"`
	FirstName    string    `json:"first_name" xml:"first-name"`
	NickName     string    `json:"nick_name" xml:"nick-name"`
	LastName     string    `json:"last_name" xml:"last-name"`
}

type ProfileApi struct {
	Path string
}

func gaeUrl() string {
	if appengine.IsDevAppServer() {
		return "http://localhost:8080"
	} else {
		// Include your URL on App Engine here.
		// I found no way to get AppID without appengine.Context and this always
		// based on a http.Request.
		return "http://federatedservices.appspot.com"
	}
}

func init() {
	u := ProfileApi{Path: "/profiles"}
	u.register()

	// Optionally, you can install the Swagger Service which provides a nice Web UI on your REST API
	// You need to download the Swagger HTML5 assets and change the FilePath location in the config below.
	// Open <your_app_id>.appspot.com/apidocs and enter
	// Place the Swagger UI files into a folder called static/swagger if you wish to use Swagger
	// http://<your_app_id>.appspot.com/apidocs.json in the api input field.
	// For testing, you can use http://localhost:8080/apidocs.json
	config := swagger.Config{
		// You control what services are visible
		WebServices:    restful.RegisteredWebServices(),
		WebServicesUrl: gaeUrl(),
		ApiPath:        "/apidocs.json",

		// Optionally, specifiy where the UI is located
		SwaggerPath: "/apidocs/",

		// GAE support static content which is configured in your app.yaml.
		// This example expect the swagger-ui in static/swagger so you should place it there :)
		SwaggerFilePath: "static/swagger"}
	swagger.InstallSwaggerService(config)
}

func (u ProfileApi) register() {
	ws := new(restful.WebService)

	ws.
		Path(u.Path).
		// You can specify consumes and produces per route as well.
		Consumes(restful.MIME_JSON, restful.MIME_XML).
		Produces(restful.MIME_JSON, restful.MIME_XML)

	ws.Route(ws.POST("").To(u.insert).
		// Swagger documentation.
		Doc("insert a new profile").
		Param(ws.BodyParameter("Profile", "representation of a profile").DataType("main.Profile")).
		Reads(Profile{}))

	ws.Route(ws.GET("/{profile-id}").To(u.read).
		// Swagger documentation.
		Doc("read a profile").
		Param(ws.PathParameter("profile-id", "identifier for a profile").DataType("string")).
		Writes(Profile{}))

	ws.Route(ws.PUT("/{profile-id}").To(u.update).
		// Swagger documentation.
		Doc("update an existing profile").
		Param(ws.PathParameter("profile-id", "identifier for a profile").DataType("string")).
		Param(ws.BodyParameter("Profile", "representation of a profile").DataType("main.Profile")).
		Reads(Profile{}))

	ws.Route(ws.DELETE("/{profile-id}").To(u.remove).
		// Swagger documentation.
		Doc("remove a profile").
		Param(ws.PathParameter("profile-id", "identifier for a profile").DataType("string")))

	restful.Add(ws)
}

// POST http://localhost:8080/profiles
// {"first_name": "Ivan", "nick_name": "Socks", "last_name": "Hawkes"}
//
func (u *ProfileApi) insert(r *restful.Request, w *restful.Response) {
	c := appengine.NewContext(r.Request)

	// Marshall the entity from the request into a struct.
	p := new(Profile)
	err := r.ReadEntity(&p)
	if err != nil {
		w.WriteError(http.StatusNotAcceptable, err)
		return
	}

	// Ensure we start with a sensible value for this field.
	p.LastModified = time.Now()

	// The profile belongs to this user.
	p.Email = user.Current(c).String()

	k, err := datastore.Put(c, datastore.NewIncompleteKey(c, "profiles", nil), p)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// Let them know the location of the newly created resource.
	// TODO: Use a safe Url path append function.
	w.AddHeader("Location", u.Path+"/"+k.Encode())

	// Return the resultant entity.
	w.WriteHeader(http.StatusCreated)
	w.WriteEntity(p)
}

// GET http://localhost:8080/profiles/ahdkZXZ-ZmVkZXJhdGlvbi1zZXJ2aWNlc3IVCxIIcHJvZmlsZXMYgICAgICAgAoM
//
func (u ProfileApi) read(r *restful.Request, w *restful.Response) {
	c := appengine.NewContext(r.Request)

	// Decode the request parameter to determine the key for the entity.
	k, err := datastore.DecodeKey(r.PathParameter("profile-id"))
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	// Retrieve the entity from the datastore.
	p := Profile{}
	if err := datastore.Get(c, k, &p); err != nil {
		if err.Error() == "datastore: no such entity" {
			http.Error(w, err.Error(), http.StatusNotFound)
		} else {
			http.Error(w, err.Error(), http.StatusInternalServerError)
		}
		return
	}

	// Check we own the profile before allowing them to view it.
	// Optionally, return a 404 instead to help prevent guessing ids.
	// TODO: Allow admins access.
	if p.Email != user.Current(c).String() {
		http.Error(w, "You do not have access to this resource", http.StatusForbidden)
		return
	}

	w.WriteEntity(p)
}

// PUT http://localhost:8080/profiles/ahdkZXZ-ZmVkZXJhdGlvbi1zZXJ2aWNlc3IVCxIIcHJvZmlsZXMYgICAgICAgAoM
// {"first_name": "Ivan", "nick_name": "Socks", "last_name": "Hawkes"}
//
func (u *ProfileApi) update(r *restful.Request, w *restful.Response) {
	c := appengine.NewContext(r.Request)

	// Decode the request parameter to determine the key for the entity.
	k, err := datastore.DecodeKey(r.PathParameter("profile-id"))
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	// Marshall the entity from the request into a struct.
	p := new(Profile)
	err = r.ReadEntity(&p)
	if err != nil {
		w.WriteError(http.StatusNotAcceptable, err)
		return
	}

	// Retrieve the old entity from the datastore.
	old := Profile{}
	if err := datastore.Get(c, k, &old); err != nil {
		if err.Error() == "datastore: no such entity" {
			http.Error(w, err.Error(), http.StatusNotFound)
		} else {
			http.Error(w, err.Error(), http.StatusInternalServerError)
		}
		return
	}

	// Check we own the profile before allowing them to update it.
	// Optionally, return a 404 instead to help prevent guessing ids.
	// TODO: Allow admins access.
	if old.Email != user.Current(c).String() {
		http.Error(w, "You do not have access to this resource", http.StatusForbidden)
		return
	}

	// Since the whole entity is re-written, we need to assign any invariant fields again
	// e.g. the owner of the entity.
	p.Email = user.Current(c).String()

	// Keep track of the last modification date.
	p.LastModified = time.Now()

	// Attempt to overwrite the old entity.
	_, err = datastore.Put(c, k, p)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// Let them know it succeeded.
	w.WriteHeader(http.StatusNoContent)
}

// DELETE http://localhost:8080/profiles/ahdkZXZ-ZmVkZXJhdGlvbi1zZXJ2aWNlc3IVCxIIcHJvZmlsZXMYgICAgICAgAoM
//
func (u *ProfileApi) remove(r *restful.Request, w *restful.Response) {
	c := appengine.NewContext(r.Request)

	// Decode the request parameter to determine the key for the entity.
	k, err := datastore.DecodeKey(r.PathParameter("profile-id"))
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	// Retrieve the old entity from the datastore.
	old := Profile{}
	if err := datastore.Get(c, k, &old); err != nil {
		if err.Error() == "datastore: no such entity" {
			http.Error(w, err.Error(), http.StatusNotFound)
		} else {
			http.Error(w, err.Error(), http.StatusInternalServerError)
		}
		return
	}

	// Check we own the profile before allowing them to delete it.
	// Optionally, return a 404 instead to help prevent guessing ids.
	// TODO: Allow admins access.
	if old.Email != user.Current(c).String() {
		http.Error(w, "You do not have access to this resource", http.StatusForbidden)
		return
	}

	// Delete the entity.
	if err := datastore.Delete(c, k); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}

	// Success notification.
	w.WriteHeader(http.StatusNoContent)
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"strconv"

	"github.com/carbocation/go.forum"
	"github.com/carbocation/go.user"
	"github.com/goods/httpbuf"
	"github.com/gorilla/context"
	"github.com/gorilla/mux"
)

type handler func(http.ResponseWriter, *http.Request) error

func (h handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	//Load session values into req
	OpenContext(req)

	//For now, print the user's info to the console all the time
	fmt.Printf("User object: %+v\n", context.Get(req, ThisUser))

	//Run the handler and grab the error, and report it. We buffer the
	// output so that handlers can modify session data at any point.
	buf := new(httpbuf.Buffer)
	if err := h(buf, req); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	//Save any changed session values
	CloseContext(req, buf)

	//apply the buffered response to the writer
	buf.Apply(w)
}

func loginHandler(w http.ResponseWriter, r *http.Request) (err error) {
	//execute the template
	data := struct {
		G    GlobalValues
		User *user.User
	}{
		globals,
		context.Get(r, ThisUser).(*user.User),
	}
	//T("login.html").Execute(w, map[string]interface{}{})
	T("login.html").Execute(w, data)
	return
}

func logoutHandler(w http.ResponseWriter, r *http.Request) (err error) {
	DeleteContext(r, w)

	http.Redirect(w, r, reverse("index"), http.StatusSeeOther)

	return
}

func indexHandler(w http.ResponseWriter, r *http.Request) (err error) {
	data := struct {
		G    GlobalValues
		User *user.User
	}{
		globals,
		context.Get(r, ThisUser).(*user.User),
	}

	T("index.html").Execute(w, data)

	return
}

func registerHandler(w http.ResponseWriter, r *http.Request) (err error) {
	data := struct {
		G        GlobalValues
		User     *user.User
		Messages []interface{}
	}{
		globals,
		context.Get(r, ThisUser).(*user.User),
		[]interface{}{},
	}

	//Don't let non-guests register again
	if !data.User.Guest() {
		http.Redirect(w, r, reverse("index"), http.StatusSeeOther)
	}

	session, _ := store.Get(r, "app")
	if flashes := session.Flashes(); len(flashes) > 0 {
		// Just print the flash values.
		data.Messages = flashes
	}

	T("register.html").Execute(w, data)
	return
}

func threadHandler(w http.ResponseWriter, r *http.Request) (err error) {
	//If the thread ID is not parseable as an integer, stop immediately
	id, err := strconv.ParseInt(mux.Vars(r)["id"], 10, 64)
	if err != nil {
		return errors.New("The requested thread is invalid.")
	}

	// Pull down the closuretable from the root requested id
	ct, err := forum.ClosureTable(id)
	if err != nil {
		fmt.Printf("main.threadHandler: %s", err)
		return errors.New("The requested thread's ancestry map could not be found.")
	}

	entries, err := forum.DescendantEntries(id)
	if err != nil {
		fmt.Printf("main.threadHandler: %s", err)
		return errors.New("The requested thread's neighbor entries could not be found.")
	}

	//Make sure this not a forum
	if entries[id].Forum {
		http.Redirect(w, r, reverse("forum", "id", id), http.StatusSeeOther)
		return
	}

	//Obligatory boxing step
	interfaceEntries := map[int64]interface{}{}
	for k, v := range entries {
		interfaceEntries[k] = v
	}

	tree, err := ct.TableToTree(interfaceEntries)
	if err != nil {
		fmt.Printf("main.threadHandler: Error converting closure table to tree: %s", err)
		return errors.New("The requested data structure could not be built.")
	}

	data := map[string]interface{}{
		"G":    globals,
		"User": context.Get(r, ThisUser).(*user.User),
		"Tree": tree,
	}

	//execute the template
	T("thread.html").Execute(w, data)

	return
}

func forumHandler(w http.ResponseWriter, r *http.Request) (err error) {
	//If the forum ID is not parseable as an integer, stop immediately
	id, err := strconv.ParseInt(mux.Vars(r)["id"], 10, 64)
	if err != nil {
		return errors.New("The requested forum is invalid.")
	}

	// Pull down the closuretable from the root requested id
	ct, err := forum.DepthOneClosureTable(id)
	fmt.Printf("%+v\n", ct)
	if err != nil {
		return errors.New("The requested forum's ancestry map could not be found.")
	}
	if ct.Size() < 1{
		return errors.New("The requested forum's ancestry map had 0 entries.")
	}

	entries, err := forum.DepthOneDescendantEntries(id)
	if err != nil {
		return errors.New("The requested forum's neighbor entries could not be found.")
	}

	//Make sure this is a forum
	if entries[id].Forum != true {
		http.Redirect(w, r, reverse("thread", "id", id), http.StatusSeeOther)
		return
	}

	//Obligatory boxing step
	interfaceEntries := map[int64]interface{}{}
	for k, v := range entries {
		interfaceEntries[k] = v
	}

	tree, err := ct.TableToTree(interfaceEntries)
	if err != nil {
		fmt.Printf("Error: %s", err)
		return errors.New("The requested data structure could not be built.")
	}

	data := map[string]interface{}{
		"G":    globals,
		"User": context.Get(r, ThisUser).(*user.User),
		"Tree": tree,
	}

	//execute the template
	T("forum.html").Execute(w, data)

	return
}

func newThreadHandler(w http.ResponseWriter, r *http.Request) (err error) {
	fmt.Fprint(w, "New thread form will go here.")

	return
}

func postLoginHandler(w http.ResponseWriter, r *http.Request) (err error) {
	r.ParseForm()

	login := new(user.User)
	//Parse the form values into the Login object
	decoder.Decode(login, r.Form)

	u, err := login.Login()
	if err != nil {
		u = new(user.User)
	}

	context.Set(r, ThisUser, u)

	//Add the user's struct to the session
	session, _ := store.Get(r, "app")
	session.Values["user"] = u

	//Redirect to a GET address to prevent form resubmission
	http.Redirect(w, r, reverse("index"), http.StatusSeeOther)

	return
}

func postRegisterHandler(w http.ResponseWriter, r *http.Request) (err error) {
	r.ParseForm()

	//Don't let non-guests register again
	if !context.Get(r, ThisUser).(*user.User).Guest() {
		http.Redirect(w, r, reverse("index"), http.StatusSeeOther)
		return
	}

	//Make sure the repeat passwords match
	if r.FormValue("PlaintextPassword") != r.FormValue("PlaintextPassword2") {
		http.Redirect(w, r, reverse("register"), http.StatusSeeOther)
		return
	}

	//Locate the session
	session, _ := store.Get(r, "app")

	//Try to create the new user in the database
	u := new(user.User)
	decoder.Decode(u, r.Form)
	err = u.Register()
	if err != nil {
		//If our registration fails for any reason, set a flag and show the form again
		//http.Redirect(w, r, reverse("register"), http.StatusSeeOther)
		context.Set(r, ThisUser, u)

		//Tell the user why we failed
		session.AddFlash(fmt.Sprintf("%s", err))

		return registerHandler(w, r)
	}

	//They're a real user. Overwrite full object by populating from the DB
	u, err = user.FindOneUserById(u.Id)
	context.Set(r, ThisUser, u)

	session.Values["user"] = u

	http.Redirect(w, r, reverse("index"), http.StatusSeeOther)

	return
}

func postThreadHandler(w http.ResponseWriter, r *http.Request) error {
	var pid int64 //parent ID
	var err error
	var parent, entry *forum.Entry

	entry = new(forum.Entry)
	r.ParseForm()
	decoder.Decode(entry, r.Form)

	fmt.Printf("%+v", entry)

	//Don't let guests post (currently)
	//TODO(james) automatically create accounts for guests who try to post
	if context.Get(r, ThisUser).(*user.User).Guest() {
		http.Error(w, "NowayBro!", http.StatusUnauthorized)

		return errors.New("Unauthorized")
	}

	//TODO(james) stop relying on the existence of a user ID here
	user := context.Get(r, ThisUser).(*user.User)
	entry.AuthorId = user.Id

	//Make sure the parent_id is valid
	if pid, err = strconv.ParseInt(r.FormValue("parent_id"), 10, 64); err != nil {
		return err
	}

	//Make sure the parent post exists
	if parent, err = forum.OneEntry(pid); err != nil {
		return err
	}

	//If posting to a forum, must have a title and body or URL.
	if parent.Forum {
		//Min length
		if len(entry.Body) < 5 {
			if u, err := url.Parse(entry.Url); err != nil {
				return errors.New("Either free-text or a URL must be provided.")
			} else {
				//The URL is valid
				entry.Url = u.String()
			}
		}
		//entry.Body is therefore valid
	} else {
		//Thread reply
		entry.Url = ""
		if len(entry.Body) < 5 {
			return errors.New("Please craft a longer reply.")
		}
		//entry.Body is therefore valid
	}

	err = entry.Persist(parent.Id)
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>Removed stray debugging output<commit_after>package main

import (
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"strconv"

	"github.com/carbocation/go.forum"
	"github.com/carbocation/go.user"
	"github.com/goods/httpbuf"
	"github.com/gorilla/context"
	"github.com/gorilla/mux"
)

type handler func(http.ResponseWriter, *http.Request) error

func (h handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	//Load session values into req
	OpenContext(req)

	//For now, print the user's info to the console all the time
	fmt.Printf("User object: %+v\n", context.Get(req, ThisUser))

	//Run the handler and grab the error, and report it. We buffer the
	// output so that handlers can modify session data at any point.
	buf := new(httpbuf.Buffer)
	if err := h(buf, req); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	//Save any changed session values
	CloseContext(req, buf)

	//apply the buffered response to the writer
	buf.Apply(w)
}

func loginHandler(w http.ResponseWriter, r *http.Request) (err error) {
	//execute the template
	data := struct {
		G    GlobalValues
		User *user.User
	}{
		globals,
		context.Get(r, ThisUser).(*user.User),
	}
	//T("login.html").Execute(w, map[string]interface{}{})
	T("login.html").Execute(w, data)
	return
}

func logoutHandler(w http.ResponseWriter, r *http.Request) (err error) {
	DeleteContext(r, w)

	http.Redirect(w, r, reverse("index"), http.StatusSeeOther)

	return
}

func indexHandler(w http.ResponseWriter, r *http.Request) (err error) {
	data := struct {
		G    GlobalValues
		User *user.User
	}{
		globals,
		context.Get(r, ThisUser).(*user.User),
	}

	T("index.html").Execute(w, data)

	return
}

func registerHandler(w http.ResponseWriter, r *http.Request) (err error) {
	data := struct {
		G        GlobalValues
		User     *user.User
		Messages []interface{}
	}{
		globals,
		context.Get(r, ThisUser).(*user.User),
		[]interface{}{},
	}

	//Don't let non-guests register again
	if !data.User.Guest() {
		http.Redirect(w, r, reverse("index"), http.StatusSeeOther)
	}

	session, _ := store.Get(r, "app")
	if flashes := session.Flashes(); len(flashes) > 0 {
		// Just print the flash values.
		data.Messages = flashes
	}

	T("register.html").Execute(w, data)
	return
}

func threadHandler(w http.ResponseWriter, r *http.Request) (err error) {
	//If the thread ID is not parseable as an integer, stop immediately
	id, err := strconv.ParseInt(mux.Vars(r)["id"], 10, 64)
	if err != nil {
		return errors.New("The requested thread is invalid.")
	}

	// Pull down the closuretable from the root requested id
	ct, err := forum.ClosureTable(id)
	if err != nil {
		fmt.Printf("main.threadHandler: %s", err)
		return errors.New("The requested thread's ancestry map could not be found.")
	}

	entries, err := forum.DescendantEntries(id)
	if err != nil {
		fmt.Printf("main.threadHandler: %s", err)
		return errors.New("The requested thread's neighbor entries could not be found.")
	}

	//Make sure this not a forum
	if entries[id].Forum {
		http.Redirect(w, r, reverse("forum", "id", id), http.StatusSeeOther)
		return
	}

	//Obligatory boxing step
	interfaceEntries := map[int64]interface{}{}
	for k, v := range entries {
		interfaceEntries[k] = v
	}

	tree, err := ct.TableToTree(interfaceEntries)
	if err != nil {
		fmt.Printf("main.threadHandler: Error converting closure table to tree: %s", err)
		return errors.New("The requested data structure could not be built.")
	}

	data := map[string]interface{}{
		"G":    globals,
		"User": context.Get(r, ThisUser).(*user.User),
		"Tree": tree,
	}

	//execute the template
	T("thread.html").Execute(w, data)

	return
}

func forumHandler(w http.ResponseWriter, r *http.Request) (err error) {
	//If the forum ID is not parseable as an integer, stop immediately
	id, err := strconv.ParseInt(mux.Vars(r)["id"], 10, 64)
	if err != nil {
		return errors.New("The requested forum is invalid.")
	}

	// Pull down the closuretable from the root requested id
	ct, err := forum.DepthOneClosureTable(id)
	if err != nil {
		return errors.New("The requested forum's ancestry map could not be found.")
	}
	if ct.Size() < 1{
		return errors.New("The requested forum's ancestry map had 0 entries.")
	}

	entries, err := forum.DepthOneDescendantEntries(id)
	if err != nil {
		return errors.New("The requested forum's neighbor entries could not be found.")
	}

	//Make sure this is a forum
	if entries[id].Forum != true {
		http.Redirect(w, r, reverse("thread", "id", id), http.StatusSeeOther)
		return
	}

	//Obligatory boxing step
	interfaceEntries := map[int64]interface{}{}
	for k, v := range entries {
		interfaceEntries[k] = v
	}

	tree, err := ct.TableToTree(interfaceEntries)
	if err != nil {
		fmt.Printf("Error: %s", err)
		return errors.New("The requested data structure could not be built.")
	}

	data := map[string]interface{}{
		"G":    globals,
		"User": context.Get(r, ThisUser).(*user.User),
		"Tree": tree,
	}

	//execute the template
	T("forum.html").Execute(w, data)

	return
}

func newThreadHandler(w http.ResponseWriter, r *http.Request) (err error) {
	fmt.Fprint(w, "New thread form will go here.")

	return
}

func postLoginHandler(w http.ResponseWriter, r *http.Request) (err error) {
	r.ParseForm()

	login := new(user.User)
	//Parse the form values into the Login object
	decoder.Decode(login, r.Form)

	u, err := login.Login()
	if err != nil {
		u = new(user.User)
	}

	context.Set(r, ThisUser, u)

	//Add the user's struct to the session
	session, _ := store.Get(r, "app")
	session.Values["user"] = u

	//Redirect to a GET address to prevent form resubmission
	http.Redirect(w, r, reverse("index"), http.StatusSeeOther)

	return
}

func postRegisterHandler(w http.ResponseWriter, r *http.Request) (err error) {
	r.ParseForm()

	//Don't let non-guests register again
	if !context.Get(r, ThisUser).(*user.User).Guest() {
		http.Redirect(w, r, reverse("index"), http.StatusSeeOther)
		return
	}

	//Make sure the repeat passwords match
	if r.FormValue("PlaintextPassword") != r.FormValue("PlaintextPassword2") {
		http.Redirect(w, r, reverse("register"), http.StatusSeeOther)
		return
	}

	//Locate the session
	session, _ := store.Get(r, "app")

	//Try to create the new user in the database
	u := new(user.User)
	decoder.Decode(u, r.Form)
	err = u.Register()
	if err != nil {
		//If our registration fails for any reason, set a flag and show the form again
		//http.Redirect(w, r, reverse("register"), http.StatusSeeOther)
		context.Set(r, ThisUser, u)

		//Tell the user why we failed
		session.AddFlash(fmt.Sprintf("%s", err))

		return registerHandler(w, r)
	}

	//They're a real user. Overwrite full object by populating from the DB
	u, err = user.FindOneUserById(u.Id)
	context.Set(r, ThisUser, u)

	session.Values["user"] = u

	http.Redirect(w, r, reverse("index"), http.StatusSeeOther)

	return
}

func postThreadHandler(w http.ResponseWriter, r *http.Request) error {
	var pid int64 //parent ID
	var err error
	var parent, entry *forum.Entry

	entry = new(forum.Entry)
	r.ParseForm()
	decoder.Decode(entry, r.Form)

	fmt.Printf("%+v", entry)

	//Don't let guests post (currently)
	//TODO(james) automatically create accounts for guests who try to post
	if context.Get(r, ThisUser).(*user.User).Guest() {
		http.Error(w, "NowayBro!", http.StatusUnauthorized)

		return errors.New("Unauthorized")
	}

	//TODO(james) stop relying on the existence of a user ID here
	user := context.Get(r, ThisUser).(*user.User)
	entry.AuthorId = user.Id

	//Make sure the parent_id is valid
	if pid, err = strconv.ParseInt(r.FormValue("parent_id"), 10, 64); err != nil {
		return err
	}

	//Make sure the parent post exists
	if parent, err = forum.OneEntry(pid); err != nil {
		return err
	}

	//If posting to a forum, must have a title and body or URL.
	if parent.Forum {
		//Min length
		if len(entry.Body) < 5 {
			if u, err := url.Parse(entry.Url); err != nil {
				return errors.New("Either free-text or a URL must be provided.")
			} else {
				//The URL is valid
				entry.Url = u.String()
			}
		}
		//entry.Body is therefore valid
	} else {
		//Thread reply
		entry.Url = ""
		if len(entry.Body) < 5 {
			return errors.New("Please craft a longer reply.")
		}
		//entry.Body is therefore valid
	}

	err = entry.Persist(parent.Id)
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package postgis

import (
	"database/sql"
	"encoding/json"
	"errors"
	"fmt"

	gostErrors "github.com/geodan/gost/src/errors"
	"github.com/geodan/gost/src/sensorthings/entities"
	"github.com/geodan/gost/src/sensorthings/odata"
)

var foiMapping = map[string]string{"feature": "public.ST_AsGeoJSON(featureofinterest.feature) AS feature"}

// GetFeatureOfInterestByLocationID returns the FeatureOfInterest in the database
// where original_location_id equals the given parameter
func (gdb *GostDatabase) GetFeatureOfInterestByLocationID(id interface{}) (*entities.FeatureOfInterest, error) {
	intID, ok := ToIntID(id)
	if !ok {
		return nil, gostErrors.NewRequestNotFound(errors.New("Location does not exist"))
	}

	sql := fmt.Sprintf("select "+CreateSelectString(&entities.FeatureOfInterest{}, nil, "", "", foiMapping)+" from %s.featureofinterest where original_location_id=%v", gdb.Schema, intID)
	return processFeatureOfInterest(gdb.Db, sql, nil)
}

// GetTotalFeaturesOfInterest returns the number of FeaturesOfInterest records in the database
func (gdb *GostDatabase) GetTotalFeaturesOfInterest() int {
	var count int
	sql := fmt.Sprintf("SELECT Count(*) from %s.featureofinterest", gdb.Schema)
	gdb.Db.QueryRow(sql).Scan(&count)
	return count
}

// GetFeatureOfInterest returns a feature of interest by id
func (gdb *GostDatabase) GetFeatureOfInterest(id interface{}, qo *odata.QueryOptions) (*entities.FeatureOfInterest, error) {
	intID, ok := ToIntID(id)
	if !ok {
		return nil, gostErrors.NewRequestNotFound(errors.New("FeatureOfInterest does not exist"))
	}

	sql := fmt.Sprintf("select "+CreateSelectString(&entities.FeatureOfInterest{}, qo, "", "", foiMapping)+" from %s.featureofinterest where id = %v", gdb.Schema, intID)
	return processFeatureOfInterest(gdb.Db, sql, qo)
}

// GetFeatureOfInterestByObservation returns a feature of interest by given observation id
func (gdb *GostDatabase) GetFeatureOfInterestByObservation(id interface{}, qo *odata.QueryOptions) (*entities.FeatureOfInterest, error) {
	intID, ok := ToIntID(id)
	if !ok {
		return nil, gostErrors.NewRequestNotFound(errors.New("Observation does not exist"))
	}

	sql := fmt.Sprintf("select "+CreateSelectString(&entities.FeatureOfInterest{}, qo, "featureofinterest.", "", foiMapping)+" from %s.featureofinterest inner join %s.observation on observation.featureofinterest_id = featureofinterest.id where observation.id = %v limit 1", gdb.Schema, gdb.Schema, intID)
	return processFeatureOfInterest(gdb.Db, sql, qo)
}

// GetFeatureOfInterests returns all feature of interests
func (gdb *GostDatabase) GetFeatureOfInterests(qo *odata.QueryOptions) ([]*entities.FeatureOfInterest, int, error) {
	sql := fmt.Sprintf("select "+CreateSelectString(&entities.FeatureOfInterest{}, qo, "", "", foiMapping)+" from %s.featureofinterest order by id desc "+CreateTopSkipQueryString(qo), gdb.Schema)
	countSSQL := fmt.Sprintf("select COUNT(*) FROM %s.featureofinterest", gdb.Schema)
	return processFeatureOfInterests(gdb.Db, sql, qo, countSSQL)
}

// PostFeatureOfInterest inserts a new FeatureOfInterest into the database
func (gdb *GostDatabase) PostFeatureOfInterest(f *entities.FeatureOfInterest) (*entities.FeatureOfInterest, error) {
	var fID int
	locationBytes, _ := json.Marshal(f.Feature)
	encoding, _ := entities.CreateEncodingType(f.EncodingType)
	sql := fmt.Sprintf("INSERT INTO %s.featureofinterest (name, description, encodingtype, feature, original_location_id) VALUES ($1, $2, $3, ST_SetSRID(public.ST_GeomFromGeoJSON('%s'),4326), $4) RETURNING id", gdb.Schema, string(locationBytes[:]))
	err := gdb.Db.QueryRow(sql, f.Name, f.Description, encoding.Code, f.OriginalLocationID).Scan(&fID)
	if err != nil {
		return nil, err
	}

	f.ID = fID
	return f, nil
}

func processFeatureOfInterest(db *sql.DB, sql string, qo *odata.QueryOptions) (*entities.FeatureOfInterest, error) {
	locations, _, err := processFeatureOfInterests(db, sql, qo, "")
	if err != nil {
		return nil, err
	}

	if len(locations) == 0 {
		return nil, gostErrors.NewRequestNotFound(errors.New("FeatureOfInterest not found"))
	}

	return locations[0], nil
}

func processFeatureOfInterests(db *sql.DB, sql string, qo *odata.QueryOptions, countSQL string) ([]*entities.FeatureOfInterest, int, error) {
	rows, err := db.Query(sql)
	defer rows.Close()

	if err != nil {
		return nil, 0, err
	}

	var featureOfInterests = []*entities.FeatureOfInterest{}
	for rows.Next() {
		var ID interface{}
		var encodingType int
		var name, description, feature string

		var params []interface{}
		var qp []string
		if qo == nil || qo.QuerySelect == nil || len(qo.QuerySelect.Params) == 0 {
			f := &entities.FeatureOfInterest{}
			qp = f.GetPropertyNames()
		} else {
			qp = qo.QuerySelect.Params
		}

		for _, p := range qp {
			if p == "id" {
				params = append(params, &ID)
			}
			if p == "encodingType" {
				params = append(params, &encodingType)
			}
			if p == "name" {
				params = append(params, &name)
			}
			if p == "description" {
				params = append(params, &description)
			}
			if p == "feature" {
				params = append(params, &feature)
			}
		}

		err = rows.Scan(params...)

		featureMap, err := JSONToMap(&feature)
		if err != nil {
			return nil, 0, err
		}

		foi := entities.FeatureOfInterest{}
		foi.ID = ID
		foi.Name = name
		foi.Description = description
		foi.Feature = featureMap
		if encodingType != 0 {
			foi.EncodingType = entities.EncodingValues[encodingType].Value
		}

		featureOfInterests = append(featureOfInterests, &foi)
	}

	var count int
	if len(countSQL) > 0 {
		db.QueryRow(countSQL).Scan(&count)
	}

	return featureOfInterests, count, nil
}

// PatchFeatureOfInterest updates a FeatureOfInterest in the database
func (gdb *GostDatabase) PatchFeatureOfInterest(id interface{}, foi *entities.FeatureOfInterest) (*entities.FeatureOfInterest, error) {
	var err error
	var ok bool
	var intID int
	updates := make(map[string]interface{})

	if intID, ok = ToIntID(id); !ok || !gdb.FeatureOfInterestExists(intID) {
		return nil, gostErrors.NewRequestNotFound(errors.New("FeatureOfInterest does not exist"))
	}

	if len(foi.Name) > 0 {
		updates["name"] = foi.Name
	}

	if len(foi.Description) > 0 {
		updates["description"] = foi.Description
	}

	if len(foi.EncodingType) > 0 {
		encoding, _ := entities.CreateEncodingType(foi.EncodingType)
		updates["encodingtype"] = encoding.Code
	}

	if len(foi.Feature) > 0 {
		locationBytes, _ := json.Marshal(foi.Feature)
		updates["feature"] = fmt.Sprintf("ST_SetSRID(public.ST_GeomFromGeoJSON('%s'),4326)", string(locationBytes[:]))
	}

	if err = gdb.updateEntityColumns("featureofinterest", updates, intID); err != nil {
		return nil, err
	}

	nfoi, _ := gdb.GetFeatureOfInterest(intID, nil)
	return nfoi, nil
}

// DeleteFeatureOfInterest tries to delete a FeatureOfInterest by the given id
func (gdb *GostDatabase) DeleteFeatureOfInterest(id interface{}) error {
	return DeleteEntity(gdb, id, "featureofinterest")
}

// FeatureOfInterestExists checks if a FeatureOfInterest is present in the database based on a given id.
func (gdb *GostDatabase) FeatureOfInterestExists(id int) bool {
	return EntityExists(gdb, id, "featureofinterest")
}
<commit_msg>remove FeatureOfInterest encodingType check<commit_after>package postgis

import (
	"database/sql"
	"encoding/json"
	"errors"
	"fmt"

	gostErrors "github.com/geodan/gost/src/errors"
	"github.com/geodan/gost/src/sensorthings/entities"
	"github.com/geodan/gost/src/sensorthings/odata"
)

var foiMapping = map[string]string{"feature": "public.ST_AsGeoJSON(featureofinterest.feature) AS feature"}

// GetFeatureOfInterestByLocationID returns the FeatureOfInterest in the database
// where original_location_id equals the given parameter
func (gdb *GostDatabase) GetFeatureOfInterestByLocationID(id interface{}) (*entities.FeatureOfInterest, error) {
	intID, ok := ToIntID(id)
	if !ok {
		return nil, gostErrors.NewRequestNotFound(errors.New("Location does not exist"))
	}

	sql := fmt.Sprintf("select "+CreateSelectString(&entities.FeatureOfInterest{}, nil, "", "", foiMapping)+" from %s.featureofinterest where original_location_id=%v", gdb.Schema, intID)
	return processFeatureOfInterest(gdb.Db, sql, nil)
}

// GetTotalFeaturesOfInterest returns the number of FeaturesOfInterest records in the database
func (gdb *GostDatabase) GetTotalFeaturesOfInterest() int {
	var count int
	sql := fmt.Sprintf("SELECT Count(*) from %s.featureofinterest", gdb.Schema)
	gdb.Db.QueryRow(sql).Scan(&count)
	return count
}

// GetFeatureOfInterest returns a feature of interest by id
func (gdb *GostDatabase) GetFeatureOfInterest(id interface{}, qo *odata.QueryOptions) (*entities.FeatureOfInterest, error) {
	intID, ok := ToIntID(id)
	if !ok {
		return nil, gostErrors.NewRequestNotFound(errors.New("FeatureOfInterest does not exist"))
	}

	sql := fmt.Sprintf("select "+CreateSelectString(&entities.FeatureOfInterest{}, qo, "", "", foiMapping)+" from %s.featureofinterest where id = %v", gdb.Schema, intID)
	return processFeatureOfInterest(gdb.Db, sql, qo)
}

// GetFeatureOfInterestByObservation returns a feature of interest by given observation id
func (gdb *GostDatabase) GetFeatureOfInterestByObservation(id interface{}, qo *odata.QueryOptions) (*entities.FeatureOfInterest, error) {
	intID, ok := ToIntID(id)
	if !ok {
		return nil, gostErrors.NewRequestNotFound(errors.New("Observation does not exist"))
	}

	sql := fmt.Sprintf("select "+CreateSelectString(&entities.FeatureOfInterest{}, qo, "featureofinterest.", "", foiMapping)+" from %s.featureofinterest inner join %s.observation on observation.featureofinterest_id = featureofinterest.id where observation.id = %v limit 1", gdb.Schema, gdb.Schema, intID)
	return processFeatureOfInterest(gdb.Db, sql, qo)
}

// GetFeatureOfInterests returns all feature of interests
func (gdb *GostDatabase) GetFeatureOfInterests(qo *odata.QueryOptions) ([]*entities.FeatureOfInterest, int, error) {
	sql := fmt.Sprintf("select "+CreateSelectString(&entities.FeatureOfInterest{}, qo, "", "", foiMapping)+" from %s.featureofinterest order by id desc "+CreateTopSkipQueryString(qo), gdb.Schema)
	countSSQL := fmt.Sprintf("select COUNT(*) FROM %s.featureofinterest", gdb.Schema)
	return processFeatureOfInterests(gdb.Db, sql, qo, countSSQL)
}

// PostFeatureOfInterest inserts a new FeatureOfInterest into the database
func (gdb *GostDatabase) PostFeatureOfInterest(f *entities.FeatureOfInterest) (*entities.FeatureOfInterest, error) {
	var fID int
	locationBytes, _ := json.Marshal(f.Feature)
	encoding, _ := entities.CreateEncodingType(f.EncodingType)
	sql := fmt.Sprintf("INSERT INTO %s.featureofinterest (name, description, encodingtype, feature, original_location_id) VALUES ($1, $2, $3, ST_SetSRID(public.ST_GeomFromGeoJSON('%s'),4326), $4) RETURNING id", gdb.Schema, string(locationBytes[:]))
	err := gdb.Db.QueryRow(sql, f.Name, f.Description, encoding.Code, f.OriginalLocationID).Scan(&fID)
	if err != nil {
		return nil, err
	}

	f.ID = fID
	return f, nil
}

func processFeatureOfInterest(db *sql.DB, sql string, qo *odata.QueryOptions) (*entities.FeatureOfInterest, error) {
	locations, _, err := processFeatureOfInterests(db, sql, qo, "")
	if err != nil {
		return nil, err
	}

	if len(locations) == 0 {
		return nil, gostErrors.NewRequestNotFound(errors.New("FeatureOfInterest not found"))
	}

	return locations[0], nil
}

func processFeatureOfInterests(db *sql.DB, sql string, qo *odata.QueryOptions, countSQL string) ([]*entities.FeatureOfInterest, int, error) {
	rows, err := db.Query(sql)
	defer rows.Close()

	if err != nil {
		return nil, 0, err
	}

	var featureOfInterests = []*entities.FeatureOfInterest{}
	for rows.Next() {
		var ID interface{}
		var encodingType int
		var name, description, feature string

		var params []interface{}
		var qp []string
		if qo == nil || qo.QuerySelect == nil || len(qo.QuerySelect.Params) == 0 {
			f := &entities.FeatureOfInterest{}
			qp = f.GetPropertyNames()
		} else {
			qp = qo.QuerySelect.Params
		}

		for _, p := range qp {
			if p == "id" {
				params = append(params, &ID)
			}
			if p == "encodingType" {
				params = append(params, &encodingType)
			}
			if p == "name" {
				params = append(params, &name)
			}
			if p == "description" {
				params = append(params, &description)
			}
			if p == "feature" {
				params = append(params, &feature)
			}
		}

		err = rows.Scan(params...)

		featureMap, err := JSONToMap(&feature)
		if err != nil {
			return nil, 0, err
		}

		foi := entities.FeatureOfInterest{}
		foi.ID = ID
		foi.Name = name
		foi.Description = description
		foi.Feature = featureMap
		foi.EncodingType = entities.EncodingValues[encodingType].Value

		featureOfInterests = append(featureOfInterests, &foi)
	}

	var count int
	if len(countSQL) > 0 {
		db.QueryRow(countSQL).Scan(&count)
	}

	return featureOfInterests, count, nil
}

// PatchFeatureOfInterest updates a FeatureOfInterest in the database
func (gdb *GostDatabase) PatchFeatureOfInterest(id interface{}, foi *entities.FeatureOfInterest) (*entities.FeatureOfInterest, error) {
	var err error
	var ok bool
	var intID int
	updates := make(map[string]interface{})

	if intID, ok = ToIntID(id); !ok || !gdb.FeatureOfInterestExists(intID) {
		return nil, gostErrors.NewRequestNotFound(errors.New("FeatureOfInterest does not exist"))
	}

	if len(foi.Name) > 0 {
		updates["name"] = foi.Name
	}

	if len(foi.Description) > 0 {
		updates["description"] = foi.Description
	}

	if len(foi.EncodingType) > 0 {
		encoding, _ := entities.CreateEncodingType(foi.EncodingType)
		updates["encodingtype"] = encoding.Code
	}

	if len(foi.Feature) > 0 {
		locationBytes, _ := json.Marshal(foi.Feature)
		updates["feature"] = fmt.Sprintf("ST_SetSRID(public.ST_GeomFromGeoJSON('%s'),4326)", string(locationBytes[:]))
	}

	if err = gdb.updateEntityColumns("featureofinterest", updates, intID); err != nil {
		return nil, err
	}

	nfoi, _ := gdb.GetFeatureOfInterest(intID, nil)
	return nfoi, nil
}

// DeleteFeatureOfInterest tries to delete a FeatureOfInterest by the given id
func (gdb *GostDatabase) DeleteFeatureOfInterest(id interface{}) error {
	return DeleteEntity(gdb, id, "featureofinterest")
}

// FeatureOfInterestExists checks if a FeatureOfInterest is present in the database based on a given id.
func (gdb *GostDatabase) FeatureOfInterestExists(id int) bool {
	return EntityExists(gdb, id, "featureofinterest")
}
<|endoftext|>


<commit_before><commit_msg>Go: update example application<commit_after><|endoftext|>


<commit_before>package gop

import (
	"fmt"
	"github.com/gorilla/mux"
	"github.com/gorilla/schema"
	"io"
	"net/http"
	"os"
	"runtime"
	"runtime/debug"
	"time"
)

var decoder = schema.NewDecoder() // Single-instance so struct info cached

func gopHandler(g *Req, w http.ResponseWriter, r *http.Request) {
	enabled, _ := g.Cfg.GetBool("gop", "enable_gop_urls", false)
	if !enabled {
		http.Error(w, "Not enabled", http.StatusNotFound)
		return
	}
	vars := mux.Vars(r)
	switch vars["action"] {
	case "status":
		{
			handleStatus(g, w, r)
			return
		}
	case "stack":
		{
			handleStack(g, w, r)
			return
		}
	case "mem":
		{
			handleMem(g, w, r)
			return
		}
	case "test":
		{
			handleTest(g, w, r)
			return
		}
	case "config":
		{
			handleConfig(g, w, r)
			return
		}
	default:
		{
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
	}
}

func handleConfig(g *Req, w http.ResponseWriter, r *http.Request) {
	configMap := g.Cfg.AsMap()
	g.SendJson(w, "config", configMap)
}

func handleMem(g *Req, w http.ResponseWriter, r *http.Request) {
	if r.Method == "POST" {
		type memParams struct {
			GCNow     int `schema:"gc_now"`
			GCPercent int `schema:"gc_percent"`
		}
		params := memParams{}
		err := g.Decoder.Decode(&params, r.Form)
		if err != nil {
			g.Error("Failed to decode params: "+err.Error(), http.StatusInternalServerError)
			http.Error(w, "Failed to decode params: "+err.Error(), http.StatusInternalServerError)
			return
		}
		msg := "Adjusting mem system\n"
		if params.GCNow > 0 {
			info := "Running GC by request to handler"
			g.Info(info)
			msg += info + "\n"

			runtime.GC()
		}
		if params.GCPercent > 0 {
			oldVal := debug.SetGCPercent(params.GCPercent)
			info := fmt.Sprintf("Set GC%% to [%d] was [%d]", params.GCPercent, oldVal)
			g.Info(info)
			msg += info + "\n"
		}
		io.WriteString(w, msg)
		return
	}
	var memStats runtime.MemStats
	runtime.ReadMemStats(&memStats)
	g.SendJson(w, "memstats", memStats)
}

func handleStack(g *Req, w http.ResponseWriter, r *http.Request) {
	buf := make([]byte, 1024)
	for {
		traceLen := runtime.Stack(buf, true)
		if traceLen < len(buf) {
			break
		}
		// Try a bigger buf
		buf = make([]byte, 2*len(buf))
	}
	w.Write(buf)
}

func handleStatus(g *Req, w http.ResponseWriter, r *http.Request) {
	type requestInfo struct {
		Id       int
		Method   string
		Url      string
		Duration float64
		RemoteIP string
		IsHTTPS  bool
	}
	type requestStatus struct {
		ProjectName   string
		AppName       string
		Pid           int
		StartTime     time.Time
		UptimeSeconds float64
		NumGoros      int
		RequestInfo   []requestInfo
	}
	appDuration := time.Since(g.app.startTime).Seconds()
	status := requestStatus{
		ProjectName:   g.app.ProjectName,
		AppName:       g.app.AppName,
		Pid:           os.Getpid(),
		StartTime:     g.app.startTime,
		UptimeSeconds: appDuration,
		NumGoros:      runtime.NumGoroutine(),
	}
	reqChan := make(chan *Req)
	g.app.getReqs <- reqChan
	for req := range reqChan {
		reqDuration := time.Since(req.startTime)
		info := requestInfo{
			Id:       req.id,
			Method:   req.r.Method,
			Url:      req.r.URL.String(),
			Duration: reqDuration.Seconds(),
			RemoteIP: req.RealRemoteIP,
			IsHTTPS:  req.IsHTTPS,
		}
		status.RequestInfo = append(status.RequestInfo, info)
	}
	g.SendJson(w, "status", status)
	/*
	   fmt.Fprintf(w, "%s - %s PID %d up for %.3fs (%s)\n\n", g.app.ProjectName, g.app.AppName, os.Getpid(), appDuration, g.app.startTime)
	   for req := range reqChan {
	       reqDuration := time.Since(req.startTime).Seconds()
	       fmt.Fprintf(w, "%d: %.3f\t%s\t%s\n", req.id, reqDuration, req.r.Method, req.r.URL.String())
	   }
	*/
}

func handleTest(g *Req, w http.ResponseWriter, r *http.Request) {
	type details struct {
		Kbytes int `schema:"kbytes"`
		Secs   int `schema:"secs"`
	}
	args := details{}
	err := g.Decoder.Decode(&args, r.Form)
	if err != nil {
		http.Error(w, "Failed to decode params: "+err.Error(), http.StatusInternalServerError)
		return
	}
	g.Debug("Test req - taking %d secs, %d KB", args.Secs, args.Kbytes)
	buf := make([]byte, args.Kbytes*1024)
	// Touch/do something with the mem to ensure it's actually allocated
	for i := range buf {
		buf[i] = 1
	}
	time.Sleep(time.Second * time.Duration(args.Secs))
	fmt.Fprintf(w, "Slow request took additional %d secs and allocated additional %d KB\n", args.Secs, args.Kbytes)
}

func (a *App) registerGopHandlers() {
	a.HandleFunc("/gop/{action}", gopHandler)
}
<commit_msg>allow config override to be set via PUT /gop/config/<section>/<key> with the body as the value. e.g.: 'echo -n newval | curl -T - http://<host>/gop/config/<section>/<key>'<commit_after>package gop

import (
	"fmt"
	"io/ioutil"
	"github.com/gorilla/mux"
	"github.com/gorilla/schema"
	"io"
	"net/http"
	"os"
	"runtime"
	"runtime/debug"
	"time"
)

var decoder = schema.NewDecoder() // Single-instance so struct info cached

func gopHandler(g *Req, w http.ResponseWriter, r *http.Request) {
	enabled, _ := g.Cfg.GetBool("gop", "enable_gop_urls", false)
	if !enabled {
		http.Error(w, "Not enabled", http.StatusNotFound)
		return
	}
	vars := mux.Vars(r)
	switch vars["action"] {
	case "status":
		{
			handleStatus(g, w, r)
			return
		}
	case "stack":
		{
			handleStack(g, w, r)
			return
		}
	case "mem":
		{
			handleMem(g, w, r)
			return
		}
	case "test":
		{
			handleTest(g, w, r)
			return
		}
	case "config":
		{
			handleConfig(g, w, r)
			return
		}
	default:
		{
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
	}
}

func handleConfig(g *Req, w http.ResponseWriter, r *http.Request) {
	// We can be called with and without section+key
	vars := mux.Vars(r)
	if r.Method == "PUT" {
		section := vars["section"]
		key := vars["key"]
		if section == "" {
			http.Error(w, "No section in url", http.StatusBadRequest)
			return
		}
		if key == "" {
			http.Error(w, "No key in url", http.StatusBadRequest)
			return
		}
		value, err := ioutil.ReadAll(r.Body)
		if err != nil {
			http.Error(w, "Failed to read value: " + err.Error(), http.StatusInternalServerError)
			return
		}
		g.Cfg.Override(section, key, string(value))
	}

	configMap := g.Cfg.AsMap()
	g.SendJson(w, "config", configMap)
}

func handleMem(g *Req, w http.ResponseWriter, r *http.Request) {
	if r.Method == "POST" {
		type memParams struct {
			GCNow     int `schema:"gc_now"`
			GCPercent int `schema:"gc_percent"`
		}
		params := memParams{}
		err := g.Decoder.Decode(&params, r.Form)
		if err != nil {
			g.Error("Failed to decode params: "+err.Error(), http.StatusInternalServerError)
			http.Error(w, "Failed to decode params: "+err.Error(), http.StatusInternalServerError)
			return
		}
		msg := "Adjusting mem system\n"
		if params.GCNow > 0 {
			info := "Running GC by request to handler"
			g.Info(info)
			msg += info + "\n"

			runtime.GC()
		}
		if params.GCPercent > 0 {
			oldVal := debug.SetGCPercent(params.GCPercent)
			info := fmt.Sprintf("Set GC%% to [%d] was [%d]", params.GCPercent, oldVal)
			g.Info(info)
			msg += info + "\n"
		}
		io.WriteString(w, msg)
		return
	}
	var memStats runtime.MemStats
	runtime.ReadMemStats(&memStats)
	g.SendJson(w, "memstats", memStats)
}

func handleStack(g *Req, w http.ResponseWriter, r *http.Request) {
	buf := make([]byte, 1024)
	for {
		traceLen := runtime.Stack(buf, true)
		if traceLen < len(buf) {
			break
		}
		// Try a bigger buf
		buf = make([]byte, 2*len(buf))
	}
	w.Write(buf)
}

func handleStatus(g *Req, w http.ResponseWriter, r *http.Request) {
	type requestInfo struct {
		Id       int
		Method   string
		Url      string
		Duration float64
		RemoteIP string
		IsHTTPS  bool
	}
	type requestStatus struct {
		ProjectName   string
		AppName       string
		Pid           int
		StartTime     time.Time
		UptimeSeconds float64
		NumGoros      int
		RequestInfo   []requestInfo
	}
	appDuration := time.Since(g.app.startTime).Seconds()
	status := requestStatus{
		ProjectName:   g.app.ProjectName,
		AppName:       g.app.AppName,
		Pid:           os.Getpid(),
		StartTime:     g.app.startTime,
		UptimeSeconds: appDuration,
		NumGoros:      runtime.NumGoroutine(),
	}
	reqChan := make(chan *Req)
	g.app.getReqs <- reqChan
	for req := range reqChan {
		reqDuration := time.Since(req.startTime)
		info := requestInfo{
			Id:       req.id,
			Method:   req.r.Method,
			Url:      req.r.URL.String(),
			Duration: reqDuration.Seconds(),
			RemoteIP: req.RealRemoteIP,
			IsHTTPS:  req.IsHTTPS,
		}
		status.RequestInfo = append(status.RequestInfo, info)
	}
	g.SendJson(w, "status", status)
	/*
	   fmt.Fprintf(w, "%s - %s PID %d up for %.3fs (%s)\n\n", g.app.ProjectName, g.app.AppName, os.Getpid(), appDuration, g.app.startTime)
	   for req := range reqChan {
	       reqDuration := time.Since(req.startTime).Seconds()
	       fmt.Fprintf(w, "%d: %.3f\t%s\t%s\n", req.id, reqDuration, req.r.Method, req.r.URL.String())
	   }
	*/
}

func handleTest(g *Req, w http.ResponseWriter, r *http.Request) {
	type details struct {
		Kbytes int `schema:"kbytes"`
		Secs   int `schema:"secs"`
	}
	args := details{}
	err := g.Decoder.Decode(&args, r.Form)
	if err != nil {
		http.Error(w, "Failed to decode params: "+err.Error(), http.StatusInternalServerError)
		return
	}
	g.Debug("Test req - taking %d secs, %d KB", args.Secs, args.Kbytes)
	buf := make([]byte, args.Kbytes*1024)
	// Touch/do something with the mem to ensure it's actually allocated
	for i := range buf {
		buf[i] = 1
	}
	time.Sleep(time.Second * time.Duration(args.Secs))
	fmt.Fprintf(w, "Slow request took additional %d secs and allocated additional %d KB\n", args.Secs, args.Kbytes)
}

func (a *App) registerGopHandlers() {
	a.HandleFunc("/gop/{action}", gopHandler)
	a.HandleFunc("/gop/config/{section}/{key}", handleConfig)
}
<|endoftext|>


<commit_before><commit_msg>Fix build flags for test file<commit_after><|endoftext|>