<commit_before>package filer

import (
	"context"
	"fmt"
	"io"
	"math/rand"
	"sync"

	"github.com/chrislusf/seaweedfs/weed/glog"
	"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
	"github.com/chrislusf/seaweedfs/weed/util"
	"github.com/chrislusf/seaweedfs/weed/util/chunk_cache"
	"github.com/chrislusf/seaweedfs/weed/wdclient"
	"github.com/golang/groupcache/singleflight"
)

type ChunkReadAt struct {
	masterClient *wdclient.MasterClient
	chunkViews   []*ChunkView
	lookupFileId wdclient.LookupFileIdFunctionType
	readerLock   sync.Mutex
	fileSize     int64

	fetchGroup      singleflight.Group
	chunkCache      chunk_cache.ChunkCache
	lastChunkFileId string
	lastChunkData   []byte
	readerPattern   *ReaderPattern
}

var _ = io.ReaderAt(&ChunkReadAt{})
var _ = io.Closer(&ChunkReadAt{})

func LookupFn(filerClient filer_pb.FilerClient) wdclient.LookupFileIdFunctionType {

	vidCache := make(map[string]*filer_pb.Locations)
	var vicCacheLock sync.RWMutex
	return func(fileId string) (targetUrls []string, err error) {
		vid := VolumeId(fileId)
		vicCacheLock.RLock()
		locations, found := vidCache[vid]
		vicCacheLock.RUnlock()

		if !found {
			util.Retry("lookup volume "+vid, func() error {
				err = filerClient.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error {
					resp, err := client.LookupVolume(context.Background(), &filer_pb.LookupVolumeRequest{
						VolumeIds: []string{vid},
					})
					if err != nil {
						return err
					}

					locations = resp.LocationsMap[vid]
					if locations == nil || len(locations.Locations) == 0 {
						glog.V(0).Infof("failed to locate %s", fileId)
						return fmt.Errorf("failed to locate %s", fileId)
					}
					vicCacheLock.Lock()
					vidCache[vid] = locations
					vicCacheLock.Unlock()

					return nil
				})
				return err
			})
		}

		if err != nil {
			return nil, err
		}

		for _, loc := range locations.Locations {
			volumeServerAddress := filerClient.AdjustedUrl(loc)
			targetUrl := fmt.Sprintf("http://%s/%s", volumeServerAddress, fileId)
			targetUrls = append(targetUrls, targetUrl)
		}

		for i := len(targetUrls) - 1; i > 0; i-- {
			j := rand.Intn(i + 1)
			targetUrls[i], targetUrls[j] = targetUrls[j], targetUrls[i]
		}

		return
	}
}

func NewChunkReaderAtFromClient(lookupFn wdclient.LookupFileIdFunctionType, chunkViews []*ChunkView, chunkCache chunk_cache.ChunkCache, fileSize int64) *ChunkReadAt {

	return &ChunkReadAt{
		chunkViews:    chunkViews,
		lookupFileId:  lookupFn,
		chunkCache:    chunkCache,
		fileSize:      fileSize,
		readerPattern: NewReaderPattern(),
	}
}

func (c *ChunkReadAt) Close() error {
	c.lastChunkData = nil
	c.lastChunkFileId = ""
	return nil
}

func (c *ChunkReadAt) ReadAt(p []byte, offset int64) (n int, err error) {

	c.readerLock.Lock()
	defer c.readerLock.Unlock()

	c.readerPattern.MonitorReadAt(offset, len(p))

	// glog.V(4).Infof("ReadAt [%d,%d) of total file size %d bytes %d chunk views", offset, offset+int64(len(p)), c.fileSize, len(c.chunkViews))
	return c.doReadAt(p, offset)
}

func (c *ChunkReadAt) doReadAt(p []byte, offset int64) (n int, err error) {

	startOffset, remaining := offset, int64(len(p))
	var nextChunk *ChunkView
	for i, chunk := range c.chunkViews {
		if remaining <= 0 {
			break
		}
		if i+1 < len(c.chunkViews) {
			nextChunk = c.chunkViews[i+1]
		} else {
			nextChunk = nil
		}
		if startOffset < chunk.LogicOffset {
			gap := int(chunk.LogicOffset - startOffset)
			glog.V(4).Infof("zero [%d,%d)", startOffset, startOffset+int64(gap))
			n += int(min(int64(gap), remaining))
			startOffset, remaining = chunk.LogicOffset, remaining-int64(gap)
			if remaining <= 0 {
				break
			}
		}
		// fmt.Printf(">>> doReadAt [%d,%d), chunk[%d,%d)\n", offset, offset+int64(len(p)), chunk.LogicOffset, chunk.LogicOffset+int64(chunk.Size))
		chunkStart, chunkStop := max(chunk.LogicOffset, startOffset), min(chunk.LogicOffset+int64(chunk.Size), startOffset+remaining)
		if chunkStart >= chunkStop {
			continue
		}
		// glog.V(4).Infof("read [%d,%d), %d/%d chunk %s [%d,%d)", chunkStart, chunkStop, i, len(c.chunkViews), chunk.FileId, chunk.LogicOffset-chunk.Offset, chunk.LogicOffset-chunk.Offset+int64(chunk.Size))
		var buffer []byte
		bufferOffset := chunkStart - chunk.LogicOffset + chunk.Offset
		bufferLength := chunkStop - chunkStart
		buffer, err = c.readChunkSlice(chunk, nextChunk, uint64(bufferOffset), uint64(bufferLength))
		if err != nil {
			glog.Errorf("fetching chunk %+v: %v\n", chunk, err)
			return
		}

		copied := copy(p[startOffset-offset:chunkStop-chunkStart+startOffset-offset], buffer)
		n += copied
		startOffset, remaining = startOffset+int64(copied), remaining-int64(copied)
	}

	// glog.V(4).Infof("doReadAt [%d,%d), n:%v, err:%v", offset, offset+int64(len(p)), n, err)

	if err == nil && remaining > 0 && c.fileSize > startOffset {
		delta := int(min(remaining, c.fileSize-startOffset))
		glog.V(4).Infof("zero2 [%d,%d) of file size %d bytes", startOffset, startOffset+int64(delta), c.fileSize)
		n += delta
	}

	if err == nil && offset+int64(len(p)) >= c.fileSize {
		err = io.EOF
	}
	// fmt.Printf("~~~ filled %d, err: %v\n\n", n, err)

	return

}

func (c *ChunkReadAt) readChunkSlice(chunkView *ChunkView, nextChunkViews *ChunkView, offset, length uint64) ([]byte, error) {

	var chunkSlice []byte
	if chunkView.LogicOffset == 0 {
		chunkSlice = c.chunkCache.GetChunkSlice(chunkView.FileId, offset, length)
	}
	if len(chunkSlice) > 0 {
		return chunkSlice, nil
	}
	if c.lookupFileId == nil {
		return nil, nil
	}
	if c.readerPattern.IsRandomMode() {
		return c.doFetchRangeChunkData(chunkView, offset, length)
	}
	chunkData, err := c.readFromWholeChunkData(chunkView, nextChunkViews)
	if err != nil {
		return nil, err
	}
	wanted := min(int64(length), int64(len(chunkData))-int64(offset))
	return chunkData[offset : int64(offset)+wanted], nil
}

func (c *ChunkReadAt) readFromWholeChunkData(chunkView *ChunkView, nextChunkViews ...*ChunkView) (chunkData []byte, err error) {

	if c.lastChunkFileId == chunkView.FileId {
		return c.lastChunkData, nil
	}

	v, doErr := c.readOneWholeChunk(chunkView)

	if doErr != nil {
		return nil, doErr
	}

	chunkData = v.([]byte)

	c.lastChunkData = chunkData
	c.lastChunkFileId = chunkView.FileId

	for _, nextChunkView := range nextChunkViews {
		if c.chunkCache != nil && nextChunkView != nil {
			go c.readOneWholeChunk(nextChunkView)
		}
	}

	return
}

func (c *ChunkReadAt) readOneWholeChunk(chunkView *ChunkView) (interface{}, error) {

	var err error

	return c.fetchGroup.Do(chunkView.FileId, func() (interface{}, error) {

		glog.V(4).Infof("readFromWholeChunkData %s offset %d [%d,%d) size at least %d", chunkView.FileId, chunkView.Offset, chunkView.LogicOffset, chunkView.LogicOffset+int64(chunkView.Size), chunkView.ChunkSize)

		var data []byte
		if chunkView.LogicOffset == 0 {
			data = c.chunkCache.GetChunk(chunkView.FileId, chunkView.ChunkSize)
		}
		if data != nil {
			glog.V(4).Infof("cache hit %s [%d,%d)", chunkView.FileId, chunkView.LogicOffset-chunkView.Offset, chunkView.LogicOffset-chunkView.Offset+int64(len(data)))
		} else {
			var err error
			data, err = c.doFetchFullChunkData(chunkView)
			if err != nil {
				return data, err
			}
			if chunkView.LogicOffset == 0 {
				// only cache the first chunk
				c.chunkCache.SetChunk(chunkView.FileId, data)
			}
		}
		return data, err
	})
}

func (c *ChunkReadAt) doFetchFullChunkData(chunkView *ChunkView) ([]byte, error) {

	glog.V(4).Infof("+ doFetchFullChunkData %s", chunkView.FileId)

	data, err := fetchChunk(c.lookupFileId, chunkView.FileId, chunkView.CipherKey, chunkView.IsGzipped)

	glog.V(4).Infof("- doFetchFullChunkData %s", chunkView.FileId)

	return data, err

}

func (c *ChunkReadAt) doFetchRangeChunkData(chunkView *ChunkView, offset, length uint64) ([]byte, error) {

	glog.V(4).Infof("+ doFetchFullChunkData %s", chunkView.FileId)

	data, err := fetchChunkRange(c.lookupFileId, chunkView.FileId, chunkView.CipherKey, chunkView.IsGzipped, int64(offset), int(length))

	glog.V(4).Infof("- doFetchFullChunkData %s", chunkView.FileId)

	return data, err

}
<commit_msg>reader: avoid wrong pattern detection due to lock waiting<commit_after>package filer

import (
	"context"
	"fmt"
	"io"
	"math/rand"
	"sync"

	"github.com/chrislusf/seaweedfs/weed/glog"
	"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
	"github.com/chrislusf/seaweedfs/weed/util"
	"github.com/chrislusf/seaweedfs/weed/util/chunk_cache"
	"github.com/chrislusf/seaweedfs/weed/wdclient"
	"github.com/golang/groupcache/singleflight"
)

type ChunkReadAt struct {
	masterClient *wdclient.MasterClient
	chunkViews   []*ChunkView
	lookupFileId wdclient.LookupFileIdFunctionType
	readerLock   sync.Mutex
	fileSize     int64

	fetchGroup      singleflight.Group
	chunkCache      chunk_cache.ChunkCache
	lastChunkFileId string
	lastChunkData   []byte
	readerPattern   *ReaderPattern
}

var _ = io.ReaderAt(&ChunkReadAt{})
var _ = io.Closer(&ChunkReadAt{})

func LookupFn(filerClient filer_pb.FilerClient) wdclient.LookupFileIdFunctionType {

	vidCache := make(map[string]*filer_pb.Locations)
	var vicCacheLock sync.RWMutex
	return func(fileId string) (targetUrls []string, err error) {
		vid := VolumeId(fileId)
		vicCacheLock.RLock()
		locations, found := vidCache[vid]
		vicCacheLock.RUnlock()

		if !found {
			util.Retry("lookup volume "+vid, func() error {
				err = filerClient.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error {
					resp, err := client.LookupVolume(context.Background(), &filer_pb.LookupVolumeRequest{
						VolumeIds: []string{vid},
					})
					if err != nil {
						return err
					}

					locations = resp.LocationsMap[vid]
					if locations == nil || len(locations.Locations) == 0 {
						glog.V(0).Infof("failed to locate %s", fileId)
						return fmt.Errorf("failed to locate %s", fileId)
					}
					vicCacheLock.Lock()
					vidCache[vid] = locations
					vicCacheLock.Unlock()

					return nil
				})
				return err
			})
		}

		if err != nil {
			return nil, err
		}

		for _, loc := range locations.Locations {
			volumeServerAddress := filerClient.AdjustedUrl(loc)
			targetUrl := fmt.Sprintf("http://%s/%s", volumeServerAddress, fileId)
			targetUrls = append(targetUrls, targetUrl)
		}

		for i := len(targetUrls) - 1; i > 0; i-- {
			j := rand.Intn(i + 1)
			targetUrls[i], targetUrls[j] = targetUrls[j], targetUrls[i]
		}

		return
	}
}

func NewChunkReaderAtFromClient(lookupFn wdclient.LookupFileIdFunctionType, chunkViews []*ChunkView, chunkCache chunk_cache.ChunkCache, fileSize int64) *ChunkReadAt {

	return &ChunkReadAt{
		chunkViews:    chunkViews,
		lookupFileId:  lookupFn,
		chunkCache:    chunkCache,
		fileSize:      fileSize,
		readerPattern: NewReaderPattern(),
	}
}

func (c *ChunkReadAt) Close() error {
	c.lastChunkData = nil
	c.lastChunkFileId = ""
	return nil
}

func (c *ChunkReadAt) ReadAt(p []byte, offset int64) (n int, err error) {

	c.readerPattern.MonitorReadAt(offset, len(p))

	c.readerLock.Lock()
	defer c.readerLock.Unlock()

	// glog.V(4).Infof("ReadAt [%d,%d) of total file size %d bytes %d chunk views", offset, offset+int64(len(p)), c.fileSize, len(c.chunkViews))
	return c.doReadAt(p, offset)
}

func (c *ChunkReadAt) doReadAt(p []byte, offset int64) (n int, err error) {

	startOffset, remaining := offset, int64(len(p))
	var nextChunk *ChunkView
	for i, chunk := range c.chunkViews {
		if remaining <= 0 {
			break
		}
		if i+1 < len(c.chunkViews) {
			nextChunk = c.chunkViews[i+1]
		} else {
			nextChunk = nil
		}
		if startOffset < chunk.LogicOffset {
			gap := int(chunk.LogicOffset - startOffset)
			glog.V(4).Infof("zero [%d,%d)", startOffset, startOffset+int64(gap))
			n += int(min(int64(gap), remaining))
			startOffset, remaining = chunk.LogicOffset, remaining-int64(gap)
			if remaining <= 0 {
				break
			}
		}
		// fmt.Printf(">>> doReadAt [%d,%d), chunk[%d,%d)\n", offset, offset+int64(len(p)), chunk.LogicOffset, chunk.LogicOffset+int64(chunk.Size))
		chunkStart, chunkStop := max(chunk.LogicOffset, startOffset), min(chunk.LogicOffset+int64(chunk.Size), startOffset+remaining)
		if chunkStart >= chunkStop {
			continue
		}
		// glog.V(4).Infof("read [%d,%d), %d/%d chunk %s [%d,%d)", chunkStart, chunkStop, i, len(c.chunkViews), chunk.FileId, chunk.LogicOffset-chunk.Offset, chunk.LogicOffset-chunk.Offset+int64(chunk.Size))
		var buffer []byte
		bufferOffset := chunkStart - chunk.LogicOffset + chunk.Offset
		bufferLength := chunkStop - chunkStart
		buffer, err = c.readChunkSlice(chunk, nextChunk, uint64(bufferOffset), uint64(bufferLength))
		if err != nil {
			glog.Errorf("fetching chunk %+v: %v\n", chunk, err)
			return
		}

		copied := copy(p[startOffset-offset:chunkStop-chunkStart+startOffset-offset], buffer)
		n += copied
		startOffset, remaining = startOffset+int64(copied), remaining-int64(copied)
	}

	// glog.V(4).Infof("doReadAt [%d,%d), n:%v, err:%v", offset, offset+int64(len(p)), n, err)

	if err == nil && remaining > 0 && c.fileSize > startOffset {
		delta := int(min(remaining, c.fileSize-startOffset))
		glog.V(4).Infof("zero2 [%d,%d) of file size %d bytes", startOffset, startOffset+int64(delta), c.fileSize)
		n += delta
	}

	if err == nil && offset+int64(len(p)) >= c.fileSize {
		err = io.EOF
	}
	// fmt.Printf("~~~ filled %d, err: %v\n\n", n, err)

	return

}

func (c *ChunkReadAt) readChunkSlice(chunkView *ChunkView, nextChunkViews *ChunkView, offset, length uint64) ([]byte, error) {

	var chunkSlice []byte
	if chunkView.LogicOffset == 0 {
		chunkSlice = c.chunkCache.GetChunkSlice(chunkView.FileId, offset, length)
	}
	if len(chunkSlice) > 0 {
		return chunkSlice, nil
	}
	if c.lookupFileId == nil {
		return nil, nil
	}
	if c.readerPattern.IsRandomMode() {
		return c.doFetchRangeChunkData(chunkView, offset, length)
	}
	chunkData, err := c.readFromWholeChunkData(chunkView, nextChunkViews)
	if err != nil {
		return nil, err
	}
	wanted := min(int64(length), int64(len(chunkData))-int64(offset))
	return chunkData[offset : int64(offset)+wanted], nil
}

func (c *ChunkReadAt) readFromWholeChunkData(chunkView *ChunkView, nextChunkViews ...*ChunkView) (chunkData []byte, err error) {

	if c.lastChunkFileId == chunkView.FileId {
		return c.lastChunkData, nil
	}

	v, doErr := c.readOneWholeChunk(chunkView)

	if doErr != nil {
		return nil, doErr
	}

	chunkData = v.([]byte)

	c.lastChunkData = chunkData
	c.lastChunkFileId = chunkView.FileId

	for _, nextChunkView := range nextChunkViews {
		if c.chunkCache != nil && nextChunkView != nil {
			go c.readOneWholeChunk(nextChunkView)
		}
	}

	return
}

func (c *ChunkReadAt) readOneWholeChunk(chunkView *ChunkView) (interface{}, error) {

	var err error

	return c.fetchGroup.Do(chunkView.FileId, func() (interface{}, error) {

		glog.V(4).Infof("readFromWholeChunkData %s offset %d [%d,%d) size at least %d", chunkView.FileId, chunkView.Offset, chunkView.LogicOffset, chunkView.LogicOffset+int64(chunkView.Size), chunkView.ChunkSize)

		var data []byte
		if chunkView.LogicOffset == 0 {
			data = c.chunkCache.GetChunk(chunkView.FileId, chunkView.ChunkSize)
		}
		if data != nil {
			glog.V(4).Infof("cache hit %s [%d,%d)", chunkView.FileId, chunkView.LogicOffset-chunkView.Offset, chunkView.LogicOffset-chunkView.Offset+int64(len(data)))
		} else {
			var err error
			data, err = c.doFetchFullChunkData(chunkView)
			if err != nil {
				return data, err
			}
			if chunkView.LogicOffset == 0 {
				// only cache the first chunk
				c.chunkCache.SetChunk(chunkView.FileId, data)
			}
		}
		return data, err
	})
}

func (c *ChunkReadAt) doFetchFullChunkData(chunkView *ChunkView) ([]byte, error) {

	glog.V(4).Infof("+ doFetchFullChunkData %s", chunkView.FileId)

	data, err := fetchChunk(c.lookupFileId, chunkView.FileId, chunkView.CipherKey, chunkView.IsGzipped)

	glog.V(4).Infof("- doFetchFullChunkData %s", chunkView.FileId)

	return data, err

}

func (c *ChunkReadAt) doFetchRangeChunkData(chunkView *ChunkView, offset, length uint64) ([]byte, error) {

	glog.V(4).Infof("+ doFetchFullChunkData %s", chunkView.FileId)

	data, err := fetchChunkRange(c.lookupFileId, chunkView.FileId, chunkView.CipherKey, chunkView.IsGzipped, int64(offset), int(length))

	glog.V(4).Infof("- doFetchFullChunkData %s", chunkView.FileId)

	return data, err

}
<|endoftext|>


<commit_before>package logging

import (
	"os"

	gologging "github.com/op/go-logging"
)

var (
	Log            Logger
	leveledBackend gologging.LeveledBackend
)

type Logger struct {
	log *gologging.Logger
}

func init() {
	Log = Logger{
		log: gologging.MustGetLogger("default"),
	}

	format := gologging.MustStringFormatter(`%{color}%{time:15:04:05.000} %{level:.4s} %{color:reset} %{message}`)
	backend := gologging.NewLogBackend(os.Stdout, "", 0)
	backendFormatter := gologging.NewBackendFormatter(backend, format)
	leveledBackend = gologging.AddModuleLevel(backendFormatter)
	gologging.SetBackend(leveledBackend)
	SetLogLevel(SILENT)
}

func SetLogLevel(l LogLevel) {
	leveledBackend.SetLevel(convertLogLevel(l), "")
}

func (l Logger) Error(msg string, err error) {
	// NOTICE: go vet doesn't like the following line:
	//   l.log.Error("%s : %v", msg, err)
	// Therefore:
	l.log.Error(""+"%s : %v", msg, err)
}

func (l Logger) Errorf(msg string, values ...interface{}) {
	l.log.Error(msg, values...)
}

func (l Logger) Info(msg string) {
	l.log.Info(msg)
}

func (l Logger) Infof(msg string, values ...interface{}) {
	l.log.Info(msg, values...)
}

func (l Logger) Debug(msg string) {
	l.log.Debug(msg)
}

func (l Logger) Debugf(msg string, values ...interface{}) {
	l.log.Debug(msg, values...)
}

func (l Logger) Panic(msg string, err error) {
	l.log.Panicf("%s : %v", msg, err)
}

func (l Logger) Panicf(msg string, values ...interface{}) {
	l.log.Panicf(msg, values...)
}

func convertLogLevel(l LogLevel) gologging.Level {
	switch l {
	case SILENT:
		return -1
	case FATAL:
		return gologging.CRITICAL
	case ERROR:
		return gologging.ERROR
	case INFO:
		return gologging.INFO
	case DEBUG:
		return gologging.DEBUG
	default:
		Log.Panicf("Unknown LogLevel: %v", l)
		return gologging.CRITICAL
	}
}
<commit_msg>Updates log format.<commit_after>package logging

import (
	"os"

	gologging "github.com/op/go-logging"
)

var (
	Log            Logger
	leveledBackend gologging.LeveledBackend
)

type Logger struct {
	log *gologging.Logger
}

func init() {
	Log = Logger{
		log: gologging.MustGetLogger("default"),
	}

	format := gologging.MustStringFormatter(`%{shortfile} %{color} %{time:2006-01-02T15:04:05.000000Z} %{level:.4s} %{color:reset} %{message}`)
	backend := gologging.NewLogBackend(os.Stdout, "", 0)
	backendFormatter := gologging.NewBackendFormatter(backend, format)
	leveledBackend = gologging.AddModuleLevel(backendFormatter)
	gologging.SetBackend(leveledBackend)
	SetLogLevel(SILENT)
}

func SetLogLevel(l LogLevel) {
	leveledBackend.SetLevel(convertLogLevel(l), "")
}

func (l Logger) Error(msg string, err error) {
	// NOTICE: go vet doesn't like the following line:
	//   l.log.Error("%s : %v", msg, err)
	// Therefore:
	l.log.Error(""+"%s : %v", msg, err)
}

func (l Logger) Errorf(msg string, values ...interface{}) {
	l.log.Error(msg, values...)
}

func (l Logger) Info(msg string) {
	l.log.Info(msg)
}

func (l Logger) Infof(msg string, values ...interface{}) {
	l.log.Info(msg, values...)
}

func (l Logger) Debug(msg string) {
	l.log.Debug(msg)
}

func (l Logger) Debugf(msg string, values ...interface{}) {
	l.log.Debug(msg, values...)
}

func (l Logger) Panic(msg string, err error) {
	l.log.Panicf("%s : %v", msg, err)
}

func (l Logger) Panicf(msg string, values ...interface{}) {
	l.log.Panicf(msg, values...)
}

func convertLogLevel(l LogLevel) gologging.Level {
	switch l {
	case SILENT:
		return -1
	case FATAL:
		return gologging.CRITICAL
	case ERROR:
		return gologging.ERROR
	case INFO:
		return gologging.INFO
	case DEBUG:
		return gologging.DEBUG
	default:
		Log.Panicf("Unknown LogLevel: %v", l)
		return gologging.CRITICAL
	}
}
<|endoftext|>


<commit_before>package sudoku

import (
	"math"
	"math/rand"
	"testing"
)

const _NUM_RUNS_TEST_WEIGHTED_DISTRIBUTION = 10000
const _ALLOWABLE_DIFF_WEIGHTED_DISTRIBUTION = 0.01

func TestRandomWeightedIndex(t *testing.T) {

	result := randomIndexWithNormalizedWeights([]float64{1.0, 0.0})
	if result != 0 {
		t.Log("Got wrong result with random weights")
		t.Fail()
	}
	result = randomIndexWithNormalizedWeights([]float64{0.5, 0.0, 0.5})
	if result != 0 && result != 2 {
		t.Log("Didn't get one of two legal weights")
		t.Fail()
	}
	result = randomIndexWithNormalizedWeights([]float64{0.0, 0.0, 1.0})
	if result != 2 {
		t.Log("Should have gotten last item in random weights; we didn't")
		t.Fail()
	}

	if weightsNormalized([]float64{1.0, 0.000001}) {
		t.Log("thought weights were normalized when they weren't")
		t.Fail()
	}
	if !weightsNormalized([]float64{0.5, 0.25, 0.25}) {
		t.Log("Didn't think weights were normalized but they were")
		t.Fail()
	}

	if weightsNormalized([]float64{0.5, -0.25, 0.25}) {
		t.Error("A negative weight was considered normal.")
	}

	rand.Seed(1)
	result = randomIndexWithInvertedWeights([]float64{0.0, 0.0, 1.0})
	if result == 2 {
		t.Error("Got the wrong index for inverted weights")
	}

	for i := 0; i < 10; i++ {
		rand.Seed(int64(i))
		result = randomIndexWithInvertedWeights([]float64{0.5, 1.0, 0.0})
		if result == 1 {
			t.Error("RandominzedIndexWithInvertedWeights returned wrong result")
			break
		}
	}

	weightResult := normalizedWeights([]float64{2.0, 1.0, 1.0})
	if weightResult[0] != 0.5 || weightResult[1] != 0.25 || weightResult[2] != 0.25 {
		t.Log("Nomralized weights came back wrong")
		t.Fail()
	}

	weightResult = normalizedWeights([]float64{1.0, 1.0, -0.5})
	if weightResult[0] != 0.5 || weightResult[1] != 0.5 || weightResult[2] != 0 {
		t.Error("Normalized weights with a negative came back wrong: ", weightResult)
	}

	weightResult = normalizedWeights([]float64{-0.25, -0.5, 0.25})
	if weightResult[0] != 0.25 || weightResult[1] != 0 || weightResult[2] != 0.75 {
		t.Error("Normalized weights with two different negative numbers came back wrong: ", weightResult)
	}

	result = randomIndexWithWeights([]float64{1.0, 0.0})
	if result != 0 {
		t.Log("Got wrong result with random weights")
		t.Fail()
	}
	result = randomIndexWithWeights([]float64{5.0, 0.0, 5.0})
	if result != 0 && result != 2 {
		t.Log("Didn't get one of two legal weights")
		t.Fail()
	}
	result = randomIndexWithWeights([]float64{0.0, 0.0, 5.0})
	if result != 2 {
		t.Log("Should have gotten last item in random weights; we didn't")
		t.Fail()
	}
	for i := 0; i < 100; i++ {
		rand.Seed(int64(i))
		result = randomIndexWithWeights([]float64{1.0, 10.0, 0.5, -1.0, 0.0, 6.4})
		if result == 3 {
			t.Error("Random index with weights picked wrong index with seed ", i)
		}
	}
	for i := 0; i < 100; i++ {
		rand.Seed(int64(i))
		result = randomIndexWithWeights([]float64{1.0, 10.0, 0.5, 1.0, 0.0, 6.4, 0.0})
		if result == 4 || result == 6 {
			t.Error("Random index with weights that ended in zero picked wrong index with seed ", i)
		}
	}
}

func TestWeightedRandomDistribution(t *testing.T) {

	//We're just going to bother testing randomIndexWithInvertedWeights since that's the one we actually use
	//in HumanSolve.

	type distributionTestCase struct {
		input       []float64
		expected    []float64
		description string
	}

	cases := []distributionTestCase{
		{
			[]float64{
				0.0,
				1.0,
				2.0,
			},
			[]float64{
				0.66666666666,
				0.33333333333,
				0.0,
			},
			"0 1 2",
		},
		{
			[]float64{
				0.0,
			},
			[]float64{
				1.0,
			},
			"0.0",
		},
		{
			[]float64{
				10.0,
			},
			[]float64{
				1.0,
			},
			"10.0",
		},
		{
			[]float64{
				0.5,
				0.5,
				1.0,
			},
			[]float64{
				0.5,
				0.5,
				0.0,
			},
			"0.5, 0.5, 1.0",
		},
		{
			[]float64{
				1.0,
				100.0,
				0.5,
				-1.0,
				0.0,
				6.4,
			},
			[]float64{
				0.20077063475968,
				0.0,
				0.20178462786453,
				0.20482660717907,
				0.20279862096938,
				0.18981950922734,
			},
			"1.0, 100.0, 0.5, -1.0, 0.0, 6.4",
		},
		{
			[]float64{
				3.0,
				3.0,
				4.0,
				4.0,
				4.0,
				4.0,
				100.0,
				100.0,
				400.0,
			},
			//TODO: think about this hard... This probability seems wrong for what we're trying to accomplish.
			// The precense of the very high numbers makes it so that the early items are picked at a similar
			//rate to the later ones that should be an order of magnitude less likely. This kind of condition
			//happens a lot in HumanSolve (all you need is one high weighted technique), and would explain why
			//we have kind of odd selections of steps.
			[]float64{
				0.13331094694426,
				0.13331094694426,
				0.13297515110813,
				0.13297515110813,
				0.13297515110813,
				0.13297515110813,
				0.10073875083949,
				0.10073875083949,
				0.0,
			},
			"Many at same weight; exponential incrase",
		},
		//This demonstrates the same problem as the case above, but is more pure
		{
			[]float64{
				0.0,
				1.0,
				2.0,
				4.0,
				8.0,
				16.0,
			},
			[]float64{
				0.24615384615385,
				0.23076923076923,
				0.21538461538462,
				0.18461538461538,
				0.12307692307692,
				0.0,
			},
			"Straight power of two increase 65",
		},
	}

	for _, testCase := range cases {
		randomIndexDistributionHelper(
			t,
			randomIndexWithInvertedWeights,
			testCase.input,
			testCase.expected,
			testCase.description)
	}

}

func randomIndexDistributionHelper(t *testing.T, theFunc func([]float64) int, input []float64, expectedDistribution []float64, testCase string) {

	if len(input) != len(expectedDistribution) {
		t.Fatal("Given differently sized input and expected distribution")
	}

	//collect the results
	results := make([]int, len(expectedDistribution))
	for i := 0; i < _NUM_RUNS_TEST_WEIGHTED_DISTRIBUTION; i++ {
		rand.Seed(int64(i))
		result := theFunc(input)
		results[result]++
	}

	//normalize the results and then calculate the diffs from expected.

	diffAccum := 0.0

	normalizedResults := make([]float64, len(results))

	for i, result := range results {
		normalizedResults[i] = float64(result) / _NUM_RUNS_TEST_WEIGHTED_DISTRIBUTION
		diffAccum += math.Abs(normalizedResults[i] - expectedDistribution[i])
	}

	if diffAccum > _ALLOWABLE_DIFF_WEIGHTED_DISTRIBUTION {
		t.Error("More than allowable difference observed in weighted random distribution:", diffAccum, testCase, "Got", normalizedResults, "Expected", expectedDistribution)
	}

}
<commit_msg>Added a note about what I'd expect the distribution for that latest case to actually be. Now need to think about how to generalize that.<commit_after>package sudoku

import (
	"math"
	"math/rand"
	"testing"
)

const _NUM_RUNS_TEST_WEIGHTED_DISTRIBUTION = 10000
const _ALLOWABLE_DIFF_WEIGHTED_DISTRIBUTION = 0.01

func TestRandomWeightedIndex(t *testing.T) {

	result := randomIndexWithNormalizedWeights([]float64{1.0, 0.0})
	if result != 0 {
		t.Log("Got wrong result with random weights")
		t.Fail()
	}
	result = randomIndexWithNormalizedWeights([]float64{0.5, 0.0, 0.5})
	if result != 0 && result != 2 {
		t.Log("Didn't get one of two legal weights")
		t.Fail()
	}
	result = randomIndexWithNormalizedWeights([]float64{0.0, 0.0, 1.0})
	if result != 2 {
		t.Log("Should have gotten last item in random weights; we didn't")
		t.Fail()
	}

	if weightsNormalized([]float64{1.0, 0.000001}) {
		t.Log("thought weights were normalized when they weren't")
		t.Fail()
	}
	if !weightsNormalized([]float64{0.5, 0.25, 0.25}) {
		t.Log("Didn't think weights were normalized but they were")
		t.Fail()
	}

	if weightsNormalized([]float64{0.5, -0.25, 0.25}) {
		t.Error("A negative weight was considered normal.")
	}

	rand.Seed(1)
	result = randomIndexWithInvertedWeights([]float64{0.0, 0.0, 1.0})
	if result == 2 {
		t.Error("Got the wrong index for inverted weights")
	}

	for i := 0; i < 10; i++ {
		rand.Seed(int64(i))
		result = randomIndexWithInvertedWeights([]float64{0.5, 1.0, 0.0})
		if result == 1 {
			t.Error("RandominzedIndexWithInvertedWeights returned wrong result")
			break
		}
	}

	weightResult := normalizedWeights([]float64{2.0, 1.0, 1.0})
	if weightResult[0] != 0.5 || weightResult[1] != 0.25 || weightResult[2] != 0.25 {
		t.Log("Nomralized weights came back wrong")
		t.Fail()
	}

	weightResult = normalizedWeights([]float64{1.0, 1.0, -0.5})
	if weightResult[0] != 0.5 || weightResult[1] != 0.5 || weightResult[2] != 0 {
		t.Error("Normalized weights with a negative came back wrong: ", weightResult)
	}

	weightResult = normalizedWeights([]float64{-0.25, -0.5, 0.25})
	if weightResult[0] != 0.25 || weightResult[1] != 0 || weightResult[2] != 0.75 {
		t.Error("Normalized weights with two different negative numbers came back wrong: ", weightResult)
	}

	result = randomIndexWithWeights([]float64{1.0, 0.0})
	if result != 0 {
		t.Log("Got wrong result with random weights")
		t.Fail()
	}
	result = randomIndexWithWeights([]float64{5.0, 0.0, 5.0})
	if result != 0 && result != 2 {
		t.Log("Didn't get one of two legal weights")
		t.Fail()
	}
	result = randomIndexWithWeights([]float64{0.0, 0.0, 5.0})
	if result != 2 {
		t.Log("Should have gotten last item in random weights; we didn't")
		t.Fail()
	}
	for i := 0; i < 100; i++ {
		rand.Seed(int64(i))
		result = randomIndexWithWeights([]float64{1.0, 10.0, 0.5, -1.0, 0.0, 6.4})
		if result == 3 {
			t.Error("Random index with weights picked wrong index with seed ", i)
		}
	}
	for i := 0; i < 100; i++ {
		rand.Seed(int64(i))
		result = randomIndexWithWeights([]float64{1.0, 10.0, 0.5, 1.0, 0.0, 6.4, 0.0})
		if result == 4 || result == 6 {
			t.Error("Random index with weights that ended in zero picked wrong index with seed ", i)
		}
	}
}

func TestWeightedRandomDistribution(t *testing.T) {

	//We're just going to bother testing randomIndexWithInvertedWeights since that's the one we actually use
	//in HumanSolve.

	type distributionTestCase struct {
		input       []float64
		expected    []float64
		description string
	}

	cases := []distributionTestCase{
		{
			[]float64{
				0.0,
				1.0,
				2.0,
			},
			[]float64{
				0.66666666666,
				0.33333333333,
				0.0,
			},
			"0 1 2",
		},
		{
			[]float64{
				0.0,
			},
			[]float64{
				1.0,
			},
			"0.0",
		},
		{
			[]float64{
				10.0,
			},
			[]float64{
				1.0,
			},
			"10.0",
		},
		{
			[]float64{
				0.5,
				0.5,
				1.0,
			},
			[]float64{
				0.5,
				0.5,
				0.0,
			},
			"0.5, 0.5, 1.0",
		},
		{
			[]float64{
				1.0,
				100.0,
				0.5,
				-1.0,
				0.0,
				6.4,
			},
			[]float64{
				0.20077063475968,
				0.0,
				0.20178462786453,
				0.20482660717907,
				0.20279862096938,
				0.18981950922734,
			},
			"1.0, 100.0, 0.5, -1.0, 0.0, 6.4",
		},
		{
			[]float64{
				3.0,
				3.0,
				4.0,
				4.0,
				4.0,
				4.0,
				100.0,
				100.0,
				400.0,
			},
			//TODO: think about this hard... This probability seems wrong for what we're trying to accomplish.
			// The precense of the very high numbers makes it so that the early items are picked at a similar
			//rate to the later ones that should be an order of magnitude less likely. This kind of condition
			//happens a lot in HumanSolve (all you need is one high weighted technique), and would explain why
			//we have kind of odd selections of steps.
			[]float64{
				0.13331094694426,
				0.13331094694426,
				0.13297515110813,
				0.13297515110813,
				0.13297515110813,
				0.13297515110813,
				0.10073875083949,
				0.10073875083949,
				0.0,
			},
			"Many at same weight; exponential incrase",
		},
		//This demonstrates the same problem as the case above, but is more pure
		{
			[]float64{
				0.0,
				1.0,
				2.0,
				4.0,
				8.0,
				16.0,
			},
			/*
				//What I'd expect the weights to be:
				//Just the weights of the input non inverted, reversed.
				//It's easier to reason about in this case since each one clearly has a dual; not
				//immediately clear how to generalize it.
				[]float64{
					0.51612903225806,
					0.25806451612903,
					0.12903225806452,
					0.06451612903226,
					0.03225806451613,
					0.0,
				},
			*/
			[]float64{
				0.24615384615385,
				0.23076923076923,
				0.21538461538462,
				0.18461538461538,
				0.12307692307692,
				0.0,
			},
			"Straight power of two increase 31",
		},
	}

	for _, testCase := range cases {
		randomIndexDistributionHelper(
			t,
			randomIndexWithInvertedWeights,
			testCase.input,
			testCase.expected,
			testCase.description)
	}

}

func randomIndexDistributionHelper(t *testing.T, theFunc func([]float64) int, input []float64, expectedDistribution []float64, testCase string) {

	if len(input) != len(expectedDistribution) {
		t.Fatal("Given differently sized input and expected distribution")
	}

	//collect the results
	results := make([]int, len(expectedDistribution))
	for i := 0; i < _NUM_RUNS_TEST_WEIGHTED_DISTRIBUTION; i++ {
		rand.Seed(int64(i))
		result := theFunc(input)
		results[result]++
	}

	//normalize the results and then calculate the diffs from expected.

	diffAccum := 0.0

	normalizedResults := make([]float64, len(results))

	for i, result := range results {
		normalizedResults[i] = float64(result) / _NUM_RUNS_TEST_WEIGHTED_DISTRIBUTION
		diffAccum += math.Abs(normalizedResults[i] - expectedDistribution[i])
	}

	if diffAccum > _ALLOWABLE_DIFF_WEIGHTED_DISTRIBUTION {
		t.Error("More than allowable difference observed in weighted random distribution:", diffAccum, testCase, "Got", normalizedResults, "Expected", expectedDistribution)
	}

}
<|endoftext|>


<commit_before>package wikifier

import (
	"regexp"
	"strconv"
	"strings"
)

var externalImageRegex = regexp.MustCompile(`^(.+)://`)

type imageBlock struct {
	file, path, alt, link, lastName string
	align, float, author, license   string
	width, height                   int
	parseFailed, useJS              bool
	*Map
}

type imagebox struct {
	*imageBlock
}

// new image{}
func newImageBlock(name string, b *parserBlock) block {
	return &imageBlock{Map: newMapBlock("", b).(*Map)}
}

// new imagebox{}
func newImagebox(name string, b *parserBlock) block {
	return &imagebox{newImageBlock(name, b).(*imageBlock)}
}

// image{} or imagebox{} parse
func (image *imageBlock) parse(page *Page) {
	image.Map.parse(page)

	// fetch string values from map
	image.file = image.getString("file")
	image.alt = image.getString("alt")
	image.link = image.getString("link")
	image.align = image.getString("align")
	image.float = image.getString("float")
	image.author = image.getString("author")
	image.license = image.getString("license")

	// fetch dimensions
	image.width = image.getPx("width")
	image.height = image.getPx("height")

	// compatibility
	if image.align == "" {
		image.align = image.getString("float")
	}

	// determine alt text
	if image.alt == "" {
		image.alt = image.file
	}

	// no dimensions. if it's an inbox we can guess it
	if image.width == 0 && image.height == 0 && image.parentBlock().blockType() == "infobox" {
		image.width = 270
	}

	// no file - this is mandatory
	if image.file == "" {
		image.warn(image.getKeyPos("file"), "No file specified for image")
		image.parseFailed = true
		return
	}

	split := strings.Split(image.file, "/")
	image.path = image.file
	image.lastName = split[len(split)-1]

	// ##############
	// ### SIZING ###
	// ##############

	sizeMethod := strings.ToLower(page.Opt.Image.SizeMethod)

	if externalImageRegex.MatchString(image.file) {
		// if the file is an absolute URL, we cannot size the image
		// do nothing

	} else if sizeMethod == "javascript" {
		// use javascript image sizing
		//
		// uses full-size images directly and uses javascript to size imageboxes
		// - voids the validity as XHTML 1.0 Strict
		// - causes slight flash on page load when images are scaled

		// inject javascript resizer if no width is given
		if image.width == 0 {
			image.useJS = true
			image.width = 200 // dummy will be overridden by javascript
		}

		// path is file relative to image root (full size image)
		image.path = page.Opt.Root.Image + "/" + image.file

	} else if sizeMethod == "server" {
		// use server-size image sizing
		//
		// - maintains XHTML 1.0 Strict validity
		// - eliminates flash on page load
		// - faster page load (since image files are smaller)
		// - require read access to local image directory

		// these must be provided by wiki
		if page.Opt.Image.Sizer == nil || page.Opt.Image.Calc == nil {
			image.warn(image.openPos, "image.sizer and image.calc required with image.size_method 'server'")
			image.parseFailed = true
			return
		}

		// determine dimensions
		calcWidth, calcHeight := page.Opt.Image.Calc(
			image.file,
			image.width,
			image.height,
			page,
		)

		// path is as returned by the function that sizes the image
		image.path = page.Opt.Image.Sizer(
			image.file,
			calcWidth,
			calcHeight,
			page,
		)

		// remember that the page uses this image in these dimensions
		page.Images[image.file] = append(page.Images[image.file], []int{calcWidth, calcHeight})

	} else {
		// note: this should never happen because the config parser validates it
		image.warn(image.openPos, "image.size_method neither 'javascript' nor 'server'")
		image.parseFailed = true
		return
	}
}

// image{} html
func (image *imageBlock) html(page *Page, el element) {
	image.imageHTML(false, page, el)
}

// imagebox{} html
func (image *imagebox) html(page *Page, el element) {
	image.imageHTML(true, page, el)
}

// image{} or imagebox{} html
func (image *imageBlock) imageHTML(isBox bool, page *Page, el element) {
	image.Map.html(page, el)

	// image parse failed, so no need to waste any time here
	if image.parseFailed {
		return
	}

	// add the appropriate float class
	if isBox {
		if image.float == "" {
			image.float = "right"
		}
		el.addClass("imagebox-" + image.float)
	} else if image.float != "" {
		el.addClass("image-" + image.float)
	}

	// retina
	// FIXME: if true -> if the image is not full-size
	srcset := ""
	if true && len(page.Opt.Image.Retina) != 0 {
		srcset = ScaleString(image.path, page.Opt.Image.Retina)
	}

	// determine link
	linkTarget := ""
	if image.link == "none" {
		// we're asked not to link to the image
		image.link = ""

	} else if image.link != "" {
		// link to something else

		// parse the link
		// ok, displaySame bool, target, display, tooltip, linkType string
		if ok, target, _, _, _ := page.parseLink(image.link); ok {
			image.link = target
			linkTarget = "_blank"
		} else {
			image.link = ""
		}

	} else {
		// link to the image

		image.link = image.path
	}

	// ############
	// ### HTML ###
	// ############

	// create an anchor for the link if there is one

	// this is just an image, no imagebox
	if !isBox {

		// put in link if there is one
		divOrA := el
		if image.link != "" {
			a := el.createChild("a", "image-a")
			a.setAttr("href", image.link)
			a.setAttr("target", linkTarget)
			divOrA = a
		}

		// create img with parent as either a or div
		img := divOrA.createChild("img", "image-img")
		img.setMeta("nonContainer", true)
		img.setAttr("src", image.path)
		img.setAttr("alt", image.alt)
		img.setAttr("srcset", srcset)

		return
	}

	// create inner box with width restriction
	inner := el.createChild("div", "imagebox-inner")
	inner.setStyle("width", strconv.Itoa(image.width)+"px")

	// put in link if there is one
	divOrA := inner
	if image.link != "" {
		a := inner.createChild("a", "image-a")
		a.setAttr("href", image.link)
		a.setAttr("target", linkTarget)
		divOrA = a
	}

	// create img with parent as either a or div
	img := divOrA.createChild("img", "imagebox-img")
	img.setMeta("nonContainer", true)
	img.setAttr("src", image.path)
	img.setAttr("alt", image.alt)
	img.setAttr("srcset", srcset)

	// insert javascript if using browser sizing
	if image.useJS {
		img.setAttr("onload", "quiki.imageResize(this);")
	}

	// description. we have to extract this here instead of in parse()
	// because at the time of parse() its text is not yet formatted
	desc, _ := image.Get("description")
	if desc == nil {
		desc, _ = image.Get("desc")
	}
	if desc != nil {
		inner.createChild(
			"div", "imagebox-description",
		).createChild(
			"div", "imagebox-description-inner",
		).add(desc)
	}
}

// fetch a string key, producing a warning at the appropriate spot if needed
func (image *imageBlock) getString(key string) string {
	s, err := image.GetStr(key)
	if err != nil {
		image.warn(image.getKeyPos(key), key+": "+err.Error())
		return ""
	}
	return s
}

// fetch a pixel size key, producing a warning at the appropriate spot if needed
func (image *imageBlock) getPx(key string) int {
	s, err := image.GetStr(key)
	if err != nil {
		image.warn(image.getKeyPos(key), key+": "+err.Error())
		return 0
	}
	if s == "" {
		return 0
	}
	i, err := strconv.Atoi(strings.TrimSuffix(s, "px"))
	if err != nil {
		image.warn(image.getKeyPos(key), key+": "+err.Error())
		return 0
	}
	return i
}
<commit_msg>no srcset for absolute urls<commit_after>package wikifier

import (
	"net/url"
	"regexp"
	"strconv"
	"strings"
)

var externalImageRegex = regexp.MustCompile(`^(.+)://`)

type imageBlock struct {
	file, path, alt, link, lastName string
	align, float, author, license   string
	width, height                   int
	parseFailed, useJS              bool
	*Map
}

type imagebox struct {
	*imageBlock
}

// new image{}
func newImageBlock(name string, b *parserBlock) block {
	return &imageBlock{Map: newMapBlock("", b).(*Map)}
}

// new imagebox{}
func newImagebox(name string, b *parserBlock) block {
	return &imagebox{newImageBlock(name, b).(*imageBlock)}
}

// image{} or imagebox{} parse
func (image *imageBlock) parse(page *Page) {
	image.Map.parse(page)

	// fetch string values from map
	image.file = image.getString("file")
	image.alt = image.getString("alt")
	image.link = image.getString("link")
	image.align = image.getString("align")
	image.float = image.getString("float")
	image.author = image.getString("author")
	image.license = image.getString("license")

	// fetch dimensions
	image.width = image.getPx("width")
	image.height = image.getPx("height")

	// compatibility
	if image.align == "" {
		image.align = image.getString("float")
	}

	// determine alt text
	if image.alt == "" {
		image.alt = image.file
	}

	// no dimensions. if it's an inbox we can guess it
	if image.width == 0 && image.height == 0 && image.parentBlock().blockType() == "infobox" {
		image.width = 270
	}

	// no file - this is mandatory
	if image.file == "" {
		image.warn(image.getKeyPos("file"), "No file specified for image")
		image.parseFailed = true
		return
	}

	split := strings.Split(image.file, "/")
	image.path = image.file
	image.lastName = split[len(split)-1]

	// ##############
	// ### SIZING ###
	// ##############

	sizeMethod := strings.ToLower(page.Opt.Image.SizeMethod)

	if externalImageRegex.MatchString(image.file) {
		// if the file is an absolute URL, we cannot size the image
		// do nothing

	} else if sizeMethod == "javascript" {
		// use javascript image sizing
		//
		// uses full-size images directly and uses javascript to size imageboxes
		// - voids the validity as XHTML 1.0 Strict
		// - causes slight flash on page load when images are scaled

		// inject javascript resizer if no width is given
		if image.width == 0 {
			image.useJS = true
			image.width = 200 // dummy will be overridden by javascript
		}

		// path is file relative to image root (full size image)
		image.path = page.Opt.Root.Image + "/" + image.file

	} else if sizeMethod == "server" {
		// use server-size image sizing
		//
		// - maintains XHTML 1.0 Strict validity
		// - eliminates flash on page load
		// - faster page load (since image files are smaller)
		// - require read access to local image directory

		// these must be provided by wiki
		if page.Opt.Image.Sizer == nil || page.Opt.Image.Calc == nil {
			image.warn(image.openPos, "image.sizer and image.calc required with image.size_method 'server'")
			image.parseFailed = true
			return
		}

		// determine dimensions
		calcWidth, calcHeight := page.Opt.Image.Calc(
			image.file,
			image.width,
			image.height,
			page,
		)

		// path is as returned by the function that sizes the image
		image.path = page.Opt.Image.Sizer(
			image.file,
			calcWidth,
			calcHeight,
			page,
		)

		// remember that the page uses this image in these dimensions
		page.Images[image.file] = append(page.Images[image.file], []int{calcWidth, calcHeight})

	} else {
		// note: this should never happen because the config parser validates it
		image.warn(image.openPos, "image.size_method neither 'javascript' nor 'server'")
		image.parseFailed = true
		return
	}
}

// image{} html
func (image *imageBlock) html(page *Page, el element) {
	image.imageHTML(false, page, el)
}

// imagebox{} html
func (image *imagebox) html(page *Page, el element) {
	image.imageHTML(true, page, el)
}

// image{} or imagebox{} html
func (image *imageBlock) imageHTML(isBox bool, page *Page, el element) {
	image.Map.html(page, el)

	// image parse failed, so no need to waste any time here
	if image.parseFailed {
		return
	}

	// add the appropriate float class
	if isBox {
		if image.float == "" {
			image.float = "right"
		}
		el.addClass("imagebox-" + image.float)
	} else if image.float != "" {
		el.addClass("image-" + image.float)
	}

	url, _ := url.Parse(image.path)
	isAbsolute := url != nil && url.IsAbs()

	// retina
	// FIXME: if true -> if the image is not full-size
	srcset := ""
	if true && !isAbsolute && len(page.Opt.Image.Retina) != 0 {
		srcset = ScaleString(image.path, page.Opt.Image.Retina)
	}

	// determine link
	linkTarget := ""
	if image.link == "none" {
		// we're asked not to link to the image
		image.link = ""

	} else if image.link != "" {
		// link to something else

		// parse the link
		// ok, displaySame bool, target, display, tooltip, linkType string
		if ok, target, _, _, _ := page.parseLink(image.link); ok {
			image.link = target
			linkTarget = "_blank"
		} else {
			image.link = ""
		}

	} else {
		// link to the image

		image.link = image.path
	}

	// ############
	// ### HTML ###
	// ############

	// create an anchor for the link if there is one

	// this is just an image, no imagebox
	if !isBox {

		// put in link if there is one
		divOrA := el
		if image.link != "" {
			a := el.createChild("a", "image-a")
			a.setAttr("href", image.link)
			a.setAttr("target", linkTarget)
			divOrA = a
		}

		// create img with parent as either a or div
		img := divOrA.createChild("img", "image-img")
		img.setMeta("nonContainer", true)
		img.setAttr("src", image.path)
		img.setAttr("alt", image.alt)
		img.setAttr("srcset", srcset)

		return
	}

	// create inner box with width restriction
	inner := el.createChild("div", "imagebox-inner")
	inner.setStyle("width", strconv.Itoa(image.width)+"px")

	// put in link if there is one
	divOrA := inner
	if image.link != "" {
		a := inner.createChild("a", "image-a")
		a.setAttr("href", image.link)
		a.setAttr("target", linkTarget)
		divOrA = a
	}

	// create img with parent as either a or div
	img := divOrA.createChild("img", "imagebox-img")
	img.setMeta("nonContainer", true)
	img.setAttr("src", image.path)
	img.setAttr("alt", image.alt)
	img.setAttr("srcset", srcset)

	// insert javascript if using browser sizing
	if image.useJS {
		img.setAttr("onload", "quiki.imageResize(this);")
	}

	// description. we have to extract this here instead of in parse()
	// because at the time of parse() its text is not yet formatted
	desc, _ := image.Get("description")
	if desc == nil {
		desc, _ = image.Get("desc")
	}
	if desc != nil {
		inner.createChild(
			"div", "imagebox-description",
		).createChild(
			"div", "imagebox-description-inner",
		).add(desc)
	}
}

// fetch a string key, producing a warning at the appropriate spot if needed
func (image *imageBlock) getString(key string) string {
	s, err := image.GetStr(key)
	if err != nil {
		image.warn(image.getKeyPos(key), key+": "+err.Error())
		return ""
	}
	return s
}

// fetch a pixel size key, producing a warning at the appropriate spot if needed
func (image *imageBlock) getPx(key string) int {
	s, err := image.GetStr(key)
	if err != nil {
		image.warn(image.getKeyPos(key), key+": "+err.Error())
		return 0
	}
	if s == "" {
		return 0
	}
	i, err := strconv.Atoi(strings.TrimSuffix(s, "px"))
	if err != nil {
		image.warn(image.getKeyPos(key), key+": "+err.Error())
		return 0
	}
	return i
}
<|endoftext|>


<commit_before>//this file, wildcard_plugin_test.go, is created by '$ ginkgo generate wildcard_plugin.go'
//VOCAB:
//GetAppsStub = func() ([]plugin_models.ApplicationSummary, error)
//
package main

import (
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/cloudfoundry/cli/plugin/models"
	//"fmt"
	"github.com/cloudfoundry/cli/plugin/fakes"
)

//top-level describe container using Ginkgo's "Describe(text string, body func()) bool" cuntion.
//var_=.. allows us to eval Describe at the top level without the need to wrap it in "func init() {}"
var _ = Describe("WildcardPlugin", func() {
	var (

		wildcardPlugin *Wildcard
		fakeCliConnection *fakes.FakeCliConnection
		s string
	)
	appsList := make([]plugin_models.ApplicationSummary, 10)

	BeforeEach(func() {
		fakeCliConnection = &fakes.FakeCliConnection{}
		wildcardPlugin = &Wildcard{}

	})
	//JustBeforeEach(func() {})

	Describe("Checking for correct results to wildcard-apps", func() {
		BeforeEach(func() {
		fakeCliConnection = &fakes.FakeCliConnection{}
		wildcardPlugin = &Wildcard{}
	})
		BeforeEach(func() {
			appsList = append(appsList,
			plugin_models.ApplicationSummary{"spring-music", "", "", 0, 0, 0, 0, nil},
			plugin_models.ApplicationSummary{"spring-master", "", "", 0, 0, 0, 0, nil},
			plugin_models.ApplicationSummary{"spring-nana", "", "", 0, 0, 0, 0, nil},
			plugin_models.ApplicationSummary{"spring-spring", "", "", 0, 0, 0, 0, nil},
			plugin_models.ApplicationSummary{"springtime", "", "", 0, 0, 0, 0, nil},
			plugin_models.ApplicationSummary{"cake", "", "", 0, 0, 0, 0, nil},
			plugin_models.ApplicationSummary{"carrot", "", "", 0, 0, 0, 0, nil},
			plugin_models.ApplicationSummary{"car", "", "", 0, 0, 0, 0, nil},
			plugin_models.ApplicationSummary{"c", "", "", 0, 0, 0, 0, nil},
			plugin_models.ApplicationSummary{"app1", "", "", 0, 0, 0, 0, nil},
			plugin_models.ApplicationSummary{"app2", "", "", 0, 0, 0, 0, nil},
			plugin_models.ApplicationSummary{"app3", "", "", 0, 0, 0, 0, nil},
			plugin_models.ApplicationSummary{"app4", "", "", 0, 0, 0, 0, nil},
			plugin_models.ApplicationSummary{"app5", "", "", 0, 0, 0, 0, nil},
			plugin_models.ApplicationSummary{"app10", "", "", 0, 0, 0, 0, nil},
			)
		})
		Context("With wildcard sp*", func() {
			It("should return all apps starting with 'sp'", func() {
				fakeCliConnection.GetAppsReturns(appsList, nil)
				//var err error
				//wildcardPlugin.WildcardCommandApps(fakeCliConnection, []string{"wc-a", "sp*"})
				output := wildcardPlugin.getMatchedApps(fakeCliConnection, []string{"wc-a", "sp*"})
				Expect(len(output)).To(Equal(5))
				Expect(output[0].Name).To(Equal("spring-music"))
				Expect(output[1].Name).To(Equal("spring-master"))
				Expect(output[2].Name).To(Equal("spring-nana"))
				Expect(output[3].Name).To(Equal("spring-spring"))
				Expect(output[4].Name).To(Equal("springtime"))

			})
			It("should return all apps starting with 'sp'", func() {
				fakeCliConnection.GetAppsReturns(appsList, nil)
				//var err error
				//wildcardPlugin.WildcardCommandApps(fakeCliConnection, []string{"wc-a", "sp*"})
				output := wildcardPlugin.getMatchedApps(fakeCliConnection, []string{"wc-a", "sp*"})
				Expect(len(output)).To(Equal(5))
				Expect(output[0].Name).To(Equal("spring-music"))
				Expect(output[1].Name).To(Equal("spring-master"))
				Expect(output[2].Name).To(Equal("spring-nana"))
				Expect(output[3].Name).To(Equal("spring-spring"))
				Expect(output[4].Name).To(Equal("springtime"))

			})
			// It("should return all apps starting with 'sp'", func() {
			// 	fakeCliConnection.GetAppsReturns(appsList, nil)
			// 	var err error
			// 	_, err = wildcardPlugin.getAppStatus(fakeCliConnection, "app1")
			// 	Expect(err.Error()).To(Equal("App app1 not found"))
			// })
		})
		Context("With wildcard sp*2", func() {
			It("should return all apps starting with 'sp'", func() {
				fakeCliConnection.GetAppsReturns(appsList, nil)
				//var err error
				//wildcardPlugin.WildcardCommandApps(fakeCliConnection, []string{"wc-a", "sp*"})
				output := wildcardPlugin.getMatchedApps(fakeCliConnection, []string{"wc-a", "sp*"})
				Expect(len(output)).To(Equal(5))
				Expect(output[0].Name).To(Equal("spring-music"))
				Expect(output[1].Name).To(Equal("spring-master"))
				Expect(output[2].Name).To(Equal("spring-nana"))
				Expect(output[3].Name).To(Equal("spring-spring"))
				Expect(output[4].Name).To(Equal("springtime"))

			})
			// It("should return all apps starting with 'sp'", func() {
			// 	fakeCliConnection.GetAppsReturns(appsList, nil)
			// 	var err error
			// 	_, err = wildcardPlugin.getAppStatus(fakeCliConnection, "app1")
			// 	Expect(err.Error()).To(Equal("App app1 not found"))
			// })
		})
		// Context("With wildcard sp*", func() {
		// 	It("should return all apps starting with 'sp'", func() {
		// 		fakeCliConnection.GetAppsReturns(appsList, nil)
		// 		var err error
		// 		_, err = wildcardPlugin.getAppStatus(fakeCliConnection, "app1")
		// 		Expect(err.Error()).To(Equal("App app1 not found"))
		// 	})
		// 	It("should return all apps starting with 'sp'", func() {
		// 		fakeCliConnection.GetAppsReturns(appsList, nil)
		// 		var err error
		// 		_, err = wildcardPlugin.getAppStatus(fakeCliConnection, "app1")
		// 		Expect(err.Error()).To(Equal("App app1 not found"))
		// 	})
		// })
	})
	// Describe("Checking for correct results to wildcard-apps", func() {
	// 	Context("With wildcard sp*", func() {
	// 		It("should return all apps starting with 'sp'", func() {
	// 			fakeCliConnection.GetAppsReturns(appsList, nil)
	// 			var err error
	// 			_, err = wildcardPlugin.getAppStatus(fakeCliConnection, "app1")
	// 			Expect(err.Error()).To(Equal("App app1 not found"))
	// 		})
	// 		It("should return all apps starting with 'sp'", func() {
	// 			fakeCliConnection.GetAppsReturns(appsList, nil)
	// 			var err error
	// 			_, err = wildcardPlugin.getAppStatus(fakeCliConnection, "app1")
	// 			Expect(err.Error()).To(Equal("App app1 not found"))
	// 		})
	// 	})
	// 	Context("With wildcard sp*", func() {
	// 		It("should return all apps starting with 'sp'", func() {
	// 			fakeCliConnection.GetAppsReturns(appsList, nil)
	// 			var err error
	// 			_, err = wildcardPlugin.getAppStatus(fakeCliConnection, "app1")
	// 			Expect(err.Error()).To(Equal("App app1 not found"))
	// 		})
	// 		It("should return all apps starting with 'sp'", func() {
	// 			fakeCliConnection.GetAppsReturns(appsList, nil)
	// 			var err error
	// 			_, err = wildcardPlugin.getAppStatus(fakeCliConnection, "app1")
	// 			Expect(err.Error()).To(Equal("App app1 not found"))
	// 		})
	// 	})
	// })
	// BeforeEach(func() {
	// 	fakeCliConnection = &fakes.FakeCliConnection{}
	// 	scaleoverCmdPlugin = &ScaleoverCmd{}
	// }

})
<commit_msg>Testing for wildcard-apps in progress. Need to add parser for table<commit_after>//this file, wildcard_plugin_test.go, is created by '$ ginkgo generate wildcard_plugin.go'
//VOCAB:
//GetAppsStub = func() ([]plugin_models.ApplicationSummary, error)
//
package main

import (
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/cloudfoundry/cli/plugin/models"
	"fmt"
	"github.com/cloudfoundry/cli/plugin/fakes"
)

//top-level describe container using Ginkgo's "Describe(text string, body func()) bool" cuntion.
//var_=.. allows us to eval Describe at the top level without the need to wrap it in "func init() {}"
var _ = Describe("WildcardPlugin", func() {
	appsList := make([]plugin_models.ApplicationSummary, 0)

	Describe("Checking for correct results to wildcard-apps", func() {
		var (
			wildcardPlugin Wildcard
			fakeCliConnection *fakes.FakeCliConnection
		)
		appsList = append(appsList,
			plugin_models.ApplicationSummary{"spring-music", "", "", 0, 0, 0, 0, nil},
			plugin_models.ApplicationSummary{"spring-master", "", "", 0, 0, 0, 0, nil},
			plugin_models.ApplicationSummary{"spring-nana", "", "", 0, 0, 0, 0, nil},
			plugin_models.ApplicationSummary{"spring-spring", "", "", 0, 0, 0, 0, nil},
			plugin_models.ApplicationSummary{"springtime", "", "", 0, 0, 0, 0, nil},
			plugin_models.ApplicationSummary{"cake", "", "", 0, 0, 0, 0, nil},
			plugin_models.ApplicationSummary{"carrot", "", "", 0, 0, 0, 0, nil},
			plugin_models.ApplicationSummary{"car", "", "", 0, 0, 0, 0, nil},
			plugin_models.ApplicationSummary{"c", "", "", 0, 0, 0, 0, nil},
			plugin_models.ApplicationSummary{"app1", "", "", 0, 0, 0, 0, nil},
			plugin_models.ApplicationSummary{"app2", "", "", 0, 0, 0, 0, nil},
			plugin_models.ApplicationSummary{"app3", "", "", 0, 0, 0, 0, nil},
			plugin_models.ApplicationSummary{"app4", "", "", 0, 0, 0, 0, nil},
			plugin_models.ApplicationSummary{"app5", "", "", 0, 0, 0, 0, nil},
			plugin_models.ApplicationSummary{"app10", "", "", 0, 0, 0, 0, nil},
			)
		BeforeEach(func() {
			fakeCliConnection = &fakes.FakeCliConnection{}
			wildcardPlugin = Wildcard{}
		})

		Context("With wildcard asterisk(*)", func() {
			It("should return all apps starting with 'ca'", func() {
				fakeCliConnection.GetAppsReturns(appsList, nil)
				output := wildcardPlugin.getMatchedApps(fakeCliConnection, []string{"wc-a", "ca*"})
				fmt.Println(output)
				Expect(len(output)).To(Equal(3))
				Expect(output[0].Name).To(Equal("cake"))
				Expect(output[1].Name).To(Equal("carrot"))
				Expect(output[2].Name).To(Equal("car"))
			})
			It("should return all apps", func() {
				fakeCliConnection.GetAppsReturns(appsList, nil)
				output := wildcardPlugin.getMatchedApps(fakeCliConnection, []string{"wc-a", "*"})
				fmt.Println(output)
				Expect(len(output)).To(Equal(15))
				Expect(output[0].Name).To(Equal("spring-music"))
				Expect(output[1].Name).To(Equal("spring-master"))
				Expect(output[2].Name).To(Equal("spring-nana"))
				Expect(output[3].Name).To(Equal("spring-spring"))
				Expect(output[4].Name).To(Equal("springtime"))
				Expect(output[5].Name).To(Equal("cake"))
				Expect(output[6].Name).To(Equal("carrot"))
				Expect(output[7].Name).To(Equal("car"))
				Expect(output[8].Name).To(Equal("c"))
				Expect(output[9].Name).To(Equal("app1"))
				Expect(output[10].Name).To(Equal("app2"))
				Expect(output[11].Name).To(Equal("app3"))
				Expect(output[12].Name).To(Equal("app4"))
				Expect(output[13].Name).To(Equal("app5"))
				Expect(output[14].Name).To(Equal("app10"))
			})
			It("should return all apps starting with 'sp'", func() {
				fakeCliConnection.GetAppsReturns(appsList, nil)
				output := wildcardPlugin.getMatchedApps(fakeCliConnection, []string{"wc-a", "sp*"})
				fmt.Println(output)
				Expect(len(output)).To(Equal(5))
				Expect(output[0].Name).To(Equal("spring-music"))
				Expect(output[1].Name).To(Equal("spring-master"))
				Expect(output[2].Name).To(Equal("spring-nana"))
				Expect(output[3].Name).To(Equal("spring-spring"))
				Expect(output[4].Name).To(Equal("springtime"))
			})
			It("should return all apps starting with 'app'", func() {
				fakeCliConnection.GetAppsReturns(appsList, nil)
				output := wildcardPlugin.getMatchedApps(fakeCliConnection, []string{"wc-a", "app*"})
				fmt.Println(output)
				Expect(len(output)).To(Equal(6))
				Expect(output[0].Name).To(Equal("app1"))
				Expect(output[1].Name).To(Equal("app2"))
				Expect(output[2].Name).To(Equal("app3"))
				Expect(output[3].Name).To(Equal("app4"))
				Expect(output[4].Name).To(Equal("app5"))
				Expect(output[5].Name).To(Equal("app10"))
			})
		})
		Context("With wildcard question-mark(?)", func() {
			It("should return all apps with patter 'ca?'", func() {
				fakeCliConnection.GetAppsReturns(appsList, nil)
				output := wildcardPlugin.getMatchedApps(fakeCliConnection, []string{"wc-a", "ca?"})
				fmt.Println(output)
				Expect(len(output)).To(Equal(1))
				Expect(output[0].Name).To(Equal("car"))
			})
			It("should return all apps with patter 'app?'", func() {
				fakeCliConnection.GetAppsReturns(appsList, nil)
				output := wildcardPlugin.getMatchedApps(fakeCliConnection, []string{"wc-a", "app?"})
				fmt.Println(output)
				Expect(len(output)).To(Equal(5))
				Expect(output[0].Name).To(Equal("app1"))
				Expect(output[1].Name).To(Equal("app2"))
				Expect(output[2].Name).To(Equal("app3"))
				Expect(output[3].Name).To(Equal("app4"))
				Expect(output[4].Name).To(Equal("app5"))
			})
			It("should return all apps with patter 'app?'", func() {
				fakeCliConnection.GetAppsReturns(appsList, nil)
				output := wildcardPlugin.getMatchedApps(fakeCliConnection, []string{"wc-a", "app??"})
				fmt.Println(output)
				Expect(len(output)).To(Equal(1))
				Expect(output[0].Name).To(Equal("app10"))
			})
		})
	})
	// Describe("Checking for correct results to wildcard-delete", func() {
	// 	Context("With wildcard sp*", func() {
	// 		It("should return all apps starting with 'sp'", func() {
	// 			fakeCliConnection.GetAppsReturns(appsList, nil)
	// 			var err error
	// 			_, err = wildcardPlugin.getAppStatus(fakeCliConnection, "app1")
	// 			Expect(err.Error()).To(Equal("App app1 not found"))
	// 		})
	// 		It("should return all apps starting with 'sp'", func() {
	// 			fakeCliConnection.GetAppsReturns(appsList, nil)
	// 			var err error
	// 			_, err = wildcardPlugin.getAppStatus(fakeCliConnection, "app1")
	// 			Expect(err.Error()).To(Equal("App app1 not found"))
	// 		})
	// 	})
	// 	Context("With wildcard sp*", func() {
	// 		It("should return all apps starting with 'sp'", func() {
	// 			fakeCliConnection.GetAppsReturns(appsList, nil)
	// 			var err error
	// 			_, err = wildcardPlugin.getAppStatus(fakeCliConnection, "app1")
	// 			Expect(err.Error()).To(Equal("App app1 not found"))
	// 		})
	// 		It("should return all apps starting with 'sp'", func() {
	// 			fakeCliConnection.GetAppsReturns(appsList, nil)
	// 			var err error
	// 			_, err = wildcardPlugin.getAppStatus(fakeCliConnection, "app1")
	// 			Expect(err.Error()).To(Equal("App app1 not found"))
	// 		})
	// 	})
	// })
	// BeforeEach(func() {
	// 	fakeCliConnection = &fakes.FakeCliConnection{}
	// 	scaleoverCmdPlugin = &ScaleoverCmd{}
	// }

})
<|endoftext|>


<commit_before>package mbtiles

import (
	"database/sql"
	_ "github.com/mattn/go-sqlite3"
	"net/http"
	"regexp"
	"strconv"
)

var zxyRegexp = regexp.MustCompile(`\A([0-9]+)/([0-9]+)/([0-9]+)\z`)

type tileServer struct {
	db   *sql.DB
	stmt *sql.Stmt
}

func NewTileServer(dsn string) (*tileServer, error) {
	db, err := sql.Open("sqlite3", dsn)
	if err != nil {
		return nil, err
	}
	stmt, err := db.Prepare("SELECT tile_data FROM tiles WHERE zoom_level = ? AND tile_column = ? AND tile_row = ?;")
	if err != nil {
		db.Close()
		return nil, err
	}
	return &tileServer{db, stmt}, nil
}

func (t *tileServer) Close() {
	t.stmt.Close()
	t.db.Close()
}

func (t *tileServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	m := zxyRegexp.FindStringSubmatch(r.URL.Path)
	if m == nil {
		http.NotFound(w, r)
		return
	}
	z, _ := strconv.Atoi(m[1])
	x, _ := strconv.Atoi(m[2])
	y, _ := strconv.Atoi(m[3])
	var tileData []byte
	if err := t.stmt.QueryRow(z, x, 1<<uint(z)-y-1).Scan(&tileData); err != nil {
		http.NotFound(w, r)
		return
	}
	w.Write(tileData)
}
<commit_msg>Run goimports<commit_after>package mbtiles

import (
	"database/sql"
	"net/http"
	"regexp"
	"strconv"

	_ "github.com/mattn/go-sqlite3"
)

var zxyRegexp = regexp.MustCompile(`\A([0-9]+)/([0-9]+)/([0-9]+)\z`)

type tileServer struct {
	db   *sql.DB
	stmt *sql.Stmt
}

func NewTileServer(dsn string) (*tileServer, error) {
	db, err := sql.Open("sqlite3", dsn)
	if err != nil {
		return nil, err
	}
	stmt, err := db.Prepare("SELECT tile_data FROM tiles WHERE zoom_level = ? AND tile_column = ? AND tile_row = ?;")
	if err != nil {
		db.Close()
		return nil, err
	}
	return &tileServer{db, stmt}, nil
}

func (t *tileServer) Close() {
	t.stmt.Close()
	t.db.Close()
}

func (t *tileServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	m := zxyRegexp.FindStringSubmatch(r.URL.Path)
	if m == nil {
		http.NotFound(w, r)
		return
	}
	z, _ := strconv.Atoi(m[1])
	x, _ := strconv.Atoi(m[2])
	y, _ := strconv.Atoi(m[3])
	var tileData []byte
	if err := t.stmt.QueryRow(z, x, 1<<uint(z)-y-1).Scan(&tileData); err != nil {
		http.NotFound(w, r)
		return
	}
	w.Write(tileData)
}
<|endoftext|>


<commit_before>// Copyright 2014 Vic Demuzere
//
// Use of this source code is governed by the MIT license.

package irc

import (
	"bytes"
	"strings"
)

// Various constants used for formatting IRC messages.
const (
	prefix     byte = 0x3A // Prefix or last argument
	prefixUser byte = 0x21 // Username
	prefixHost byte = 0x40 // Hostname
	space      byte = 0x20 // Separator

	cutset string = "\r\n\x20\x00" // Characters to trim from prefixes/messages.

	maxLength = 510 // Maximum length is 512 - 2 for the line endings.
)

// Objects implementing the Sender interface are able to send messages to an IRC server.
//
// As there might be a message queue, it is possible that Send returns a nil
// error, but the message is not sent (yet). The error value is only used when
// it is certain that sending the message is impossible.
//
// This interface is not used inside this package, and shouldn't have been
// defined here in the first place. For backwards compatibility only.
type Sender interface {
	Send(*Message) error
}

// Prefix represents the prefix (sender) of an IRC message.
// See RFC1459 section 2.3.1.
//
//    <servername> | <nick> [ '!' <user> ] [ '@' <host> ]
//
type Prefix struct {
	Name string // Nick- or servername
	User string // Username
	Host string // Hostname
}

// ParsePrefix takes a string and attempts to create a Prefix struct.
func ParsePrefix(raw string) (p *Prefix) {

	p = new(Prefix)

	user := indexByte(raw, prefixUser)
	host := indexByte(raw, prefixHost)

	switch {

	case user > 0 && host > user:
		p.Name = raw[:user]
		p.User = raw[user+1 : host]
		p.Host = raw[host+1:]

	case user > 0:
		p.Name = raw[:user]
		p.User = raw[user+1:]

	case host > 0:
		p.Name = raw[:host]
		p.Host = raw[host+1:]

	default:
		p.Name = raw

	}

	return p
}

// Len calculates the length of the string representation of this prefix.
func (p *Prefix) Len() (length int) {
	length = len(p.Name)
	if len(p.User) > 0 {
		length = length + len(p.User) + 1
	}
	if len(p.Host) > 0 {
		length = length + len(p.Host) + 1
	}
	return
}

// String returns a string representation of this prefix.
func (p *Prefix) String() (s string) {
	// Benchmarks revealed that in this case simple string concatenation
	// is actually faster than using a ByteBuffer as in (*Message).String()
	s = p.Name
	if len(p.User) > 0 {
		s = s + string(prefixUser) + p.User
	}
	if len(p.Host) > 0 {
		s = s + string(prefixHost) + p.Host
	}
	return
}

// IsHostmask returns true if this prefix looks like a user hostmask.
func (p *Prefix) IsHostmask() bool {
	return len(p.User) > 0 && len(p.Host) > 0
}

// IsServer returns true if this prefix looks like a server name.
func (p *Prefix) IsServer() bool {
	return len(p.User) <= 0 && len(p.Host) <= 0 // && indexByte(p.Name, '.') > 0
}

// writeTo is an utility function to write the prefix to the bytes.Buffer in Message.String().
func (p *Prefix) writeTo(buffer *bytes.Buffer) {
	buffer.WriteString(p.Name)
	if len(p.User) > 0 {
		buffer.WriteByte(prefixUser)
		buffer.WriteString(p.User)
	}
	if len(p.Host) > 0 {
		buffer.WriteByte(prefixHost)
		buffer.WriteString(p.Host)
	}
	return
}

// Message represents an IRC protocol message.
// See RFC1459 section 2.3.1.
//
//    <message>  ::= [':' <prefix> <SPACE> ] <command> <params> <crlf>
//    <prefix>   ::= <servername> | <nick> [ '!' <user> ] [ '@' <host> ]
//    <command>  ::= <letter> { <letter> } | <number> <number> <number>
//    <SPACE>    ::= ' ' { ' ' }
//    <params>   ::= <SPACE> [ ':' <trailing> | <middle> <params> ]
//
//    <middle>   ::= <Any *non-empty* sequence of octets not including SPACE
//                   or NUL or CR or LF, the first of which may not be ':'>
//    <trailing> ::= <Any, possibly *empty*, sequence of octets not including
//                   NUL or CR or LF>
//
//    <crlf>     ::= CR LF
type Message struct {
	*Prefix
	Command  string
	Params   []string
	Trailing string

	// When set to true, the trailing prefix (:) will be added even if the trailing message is empty.
	EmptyTrailing bool
}

// ParseMessage takes a string and attempts to create a Message struct.
// Returns nil if the Message is invalid.
func ParseMessage(raw string) (m *Message) {

	// Ignore empty messages.
	if raw = strings.Trim(raw, cutset); len(raw) < 2 {
		return nil
	}

	i, j := 0, 0

	m = new(Message)

	if raw[0] == prefix {

		// Prefix ends with a space.
		i = indexByte(raw, space)

		// Prefix string must not be empty if the indicator is present.
		if i < 2 {
			return nil
		}

		m.Prefix = ParsePrefix(raw[1:i])

		// Skip space at the end of the prefix
		i++
	}

	// Find end of command
	j = i + indexByte(raw[i:], space)

	// Extract command
	if j > i {
		m.Command = raw[i:j]
	} else {
		m.Command = raw[i:]

		// We're done here!
		return m
	}

	// Skip space after command
	j++

	// Find prefix for trailer
	i = indexByte(raw[j:], prefix)

	if i < 0 {

		// There is no trailing argument!
		m.Params = strings.Split(raw[j:], string(space))

		// We're done here!
		return m
	}

	// Compensate for index on substring
	i = i + j

	// Check if we need to parse arguments.
	if i > j {
		m.Params = strings.Split(raw[j:i-1], string(space))
	}

	m.Trailing = raw[i+1:]

	// We need to re-encode the trailing argument even if it was empty.
	if len(m.Trailing) <= 0 {
		m.EmptyTrailing = true
	}

	return m

}

// Len calculates the length of the string representation of this message.
func (m *Message) Len() (length int) {

	if m.Prefix != nil {
		length = m.Prefix.Len() + 2 // Include prefix and trailing space
	}

	length = length + len(m.Command)

	if len(m.Params) > 0 {
		length = length + len(m.Params)
		for _, param := range m.Params {
			length = length + len(param)
		}
	}

	if len(m.Trailing) > 0 || m.EmptyTrailing {
		length = length + len(m.Trailing) + 2 // Include prefix and space
	}

	return
}

// Bytes returns a []byte representation of this message.
//
// As noted in rfc2812 section 2.3, messages should not exceed 512 characters
// in length. This method forces that limit by discarding any characters
// exceeding the length limit.
func (m *Message) Bytes() []byte {

	buffer := new(bytes.Buffer)

	// Message prefix
	if m.Prefix != nil {
		buffer.WriteByte(prefix)
		m.Prefix.writeTo(buffer)
		buffer.WriteByte(space)
	}

	// Command is required
	buffer.WriteString(m.Command)

	// Space separated list of arguments
	if len(m.Params) > 0 {
		buffer.WriteByte(space)
		buffer.WriteString(strings.Join(m.Params, string(space)))
	}

	if len(m.Trailing) > 0 || m.EmptyTrailing {
		buffer.WriteByte(space)
		buffer.WriteByte(prefix)
		buffer.WriteString(m.Trailing)
	}

	// We need the limit the buffer length.
	if buffer.Len() > (maxLength) {
		buffer.Truncate(maxLength)
	}

	return buffer.Bytes()
}

// String returns a string representation of this message.
//
// As noted in rfc2812 section 2.3, messages should not exceed 512 characters
// in length. This method forces that limit by discarding any characters
// exceeding the length limit.
func (m *Message) String() string {
	return string(m.Bytes())
}
<commit_msg>Use strings.TrimFunc instead of strings.Trim.<commit_after>// Copyright 2014 Vic Demuzere
//
// Use of this source code is governed by the MIT license.

package irc

import (
	"bytes"
	"strings"
)

// Various constants used for formatting IRC messages.
const (
	prefix     byte = 0x3A // Prefix or last argument
	prefixUser byte = 0x21 // Username
	prefixHost byte = 0x40 // Hostname
	space      byte = 0x20 // Separator

	maxLength = 510 // Maximum length is 512 - 2 for the line endings.
)

func cutsetFunc(r rune) bool {
	// Characters to trim from prefixes/messages.
	return r == '\r' || r == '\n' || r == '\x20' || r == '\x00'
}

// Objects implementing the Sender interface are able to send messages to an IRC server.
//
// As there might be a message queue, it is possible that Send returns a nil
// error, but the message is not sent (yet). The error value is only used when
// it is certain that sending the message is impossible.
//
// This interface is not used inside this package, and shouldn't have been
// defined here in the first place. For backwards compatibility only.
type Sender interface {
	Send(*Message) error
}

// Prefix represents the prefix (sender) of an IRC message.
// See RFC1459 section 2.3.1.
//
//    <servername> | <nick> [ '!' <user> ] [ '@' <host> ]
//
type Prefix struct {
	Name string // Nick- or servername
	User string // Username
	Host string // Hostname
}

// ParsePrefix takes a string and attempts to create a Prefix struct.
func ParsePrefix(raw string) (p *Prefix) {

	p = new(Prefix)

	user := indexByte(raw, prefixUser)
	host := indexByte(raw, prefixHost)

	switch {

	case user > 0 && host > user:
		p.Name = raw[:user]
		p.User = raw[user+1 : host]
		p.Host = raw[host+1:]

	case user > 0:
		p.Name = raw[:user]
		p.User = raw[user+1:]

	case host > 0:
		p.Name = raw[:host]
		p.Host = raw[host+1:]

	default:
		p.Name = raw

	}

	return p
}

// Len calculates the length of the string representation of this prefix.
func (p *Prefix) Len() (length int) {
	length = len(p.Name)
	if len(p.User) > 0 {
		length = length + len(p.User) + 1
	}
	if len(p.Host) > 0 {
		length = length + len(p.Host) + 1
	}
	return
}

// String returns a string representation of this prefix.
func (p *Prefix) String() (s string) {
	// Benchmarks revealed that in this case simple string concatenation
	// is actually faster than using a ByteBuffer as in (*Message).String()
	s = p.Name
	if len(p.User) > 0 {
		s = s + string(prefixUser) + p.User
	}
	if len(p.Host) > 0 {
		s = s + string(prefixHost) + p.Host
	}
	return
}

// IsHostmask returns true if this prefix looks like a user hostmask.
func (p *Prefix) IsHostmask() bool {
	return len(p.User) > 0 && len(p.Host) > 0
}

// IsServer returns true if this prefix looks like a server name.
func (p *Prefix) IsServer() bool {
	return len(p.User) <= 0 && len(p.Host) <= 0 // && indexByte(p.Name, '.') > 0
}

// writeTo is an utility function to write the prefix to the bytes.Buffer in Message.String().
func (p *Prefix) writeTo(buffer *bytes.Buffer) {
	buffer.WriteString(p.Name)
	if len(p.User) > 0 {
		buffer.WriteByte(prefixUser)
		buffer.WriteString(p.User)
	}
	if len(p.Host) > 0 {
		buffer.WriteByte(prefixHost)
		buffer.WriteString(p.Host)
	}
	return
}

// Message represents an IRC protocol message.
// See RFC1459 section 2.3.1.
//
//    <message>  ::= [':' <prefix> <SPACE> ] <command> <params> <crlf>
//    <prefix>   ::= <servername> | <nick> [ '!' <user> ] [ '@' <host> ]
//    <command>  ::= <letter> { <letter> } | <number> <number> <number>
//    <SPACE>    ::= ' ' { ' ' }
//    <params>   ::= <SPACE> [ ':' <trailing> | <middle> <params> ]
//
//    <middle>   ::= <Any *non-empty* sequence of octets not including SPACE
//                   or NUL or CR or LF, the first of which may not be ':'>
//    <trailing> ::= <Any, possibly *empty*, sequence of octets not including
//                   NUL or CR or LF>
//
//    <crlf>     ::= CR LF
type Message struct {
	*Prefix
	Command  string
	Params   []string
	Trailing string

	// When set to true, the trailing prefix (:) will be added even if the trailing message is empty.
	EmptyTrailing bool
}

// ParseMessage takes a string and attempts to create a Message struct.
// Returns nil if the Message is invalid.
func ParseMessage(raw string) (m *Message) {

	// Ignore empty messages.
	if raw = strings.TrimFunc(raw, cutsetFunc); len(raw) < 2 {
		return nil
	}

	i, j := 0, 0

	m = new(Message)

	if raw[0] == prefix {

		// Prefix ends with a space.
		i = indexByte(raw, space)

		// Prefix string must not be empty if the indicator is present.
		if i < 2 {
			return nil
		}

		m.Prefix = ParsePrefix(raw[1:i])

		// Skip space at the end of the prefix
		i++
	}

	// Find end of command
	j = i + indexByte(raw[i:], space)

	// Extract command
	if j > i {
		m.Command = raw[i:j]
	} else {
		m.Command = raw[i:]

		// We're done here!
		return m
	}

	// Skip space after command
	j++

	// Find prefix for trailer
	i = indexByte(raw[j:], prefix)

	if i < 0 {

		// There is no trailing argument!
		m.Params = strings.Split(raw[j:], string(space))

		// We're done here!
		return m
	}

	// Compensate for index on substring
	i = i + j

	// Check if we need to parse arguments.
	if i > j {
		m.Params = strings.Split(raw[j:i-1], string(space))
	}

	m.Trailing = raw[i+1:]

	// We need to re-encode the trailing argument even if it was empty.
	if len(m.Trailing) <= 0 {
		m.EmptyTrailing = true
	}

	return m

}

// Len calculates the length of the string representation of this message.
func (m *Message) Len() (length int) {

	if m.Prefix != nil {
		length = m.Prefix.Len() + 2 // Include prefix and trailing space
	}

	length = length + len(m.Command)

	if len(m.Params) > 0 {
		length = length + len(m.Params)
		for _, param := range m.Params {
			length = length + len(param)
		}
	}

	if len(m.Trailing) > 0 || m.EmptyTrailing {
		length = length + len(m.Trailing) + 2 // Include prefix and space
	}

	return
}

// Bytes returns a []byte representation of this message.
//
// As noted in rfc2812 section 2.3, messages should not exceed 512 characters
// in length. This method forces that limit by discarding any characters
// exceeding the length limit.
func (m *Message) Bytes() []byte {

	buffer := new(bytes.Buffer)

	// Message prefix
	if m.Prefix != nil {
		buffer.WriteByte(prefix)
		m.Prefix.writeTo(buffer)
		buffer.WriteByte(space)
	}

	// Command is required
	buffer.WriteString(m.Command)

	// Space separated list of arguments
	if len(m.Params) > 0 {
		buffer.WriteByte(space)
		buffer.WriteString(strings.Join(m.Params, string(space)))
	}

	if len(m.Trailing) > 0 || m.EmptyTrailing {
		buffer.WriteByte(space)
		buffer.WriteByte(prefix)
		buffer.WriteString(m.Trailing)
	}

	// We need the limit the buffer length.
	if buffer.Len() > (maxLength) {
		buffer.Truncate(maxLength)
	}

	return buffer.Bytes()
}

// String returns a string representation of this message.
//
// As noted in rfc2812 section 2.3, messages should not exceed 512 characters
// in length. This method forces that limit by discarding any characters
// exceeding the length limit.
func (m *Message) String() string {
	return string(m.Bytes())
}
<|endoftext|>


<commit_before>/**
 * Copyright 2014 @ S1N1 Team.
 * name :
 * author : jarryliu
 * date : 2014-02-05 21:53
 * description :
 * history :
 */
package partner

import (
	"encoding/json"
	"github.com/atnet/gof"
	"github.com/atnet/gof/web"
	"github.com/atnet/gof/web/mvc"
	"go2o/src/core/service/dps"
	"html/template"
	"time"
	"strconv"
	"go2o/src/core/domain/interface/content"
)

var _ mvc.Filter = new(adC)

// 广告控制器
type adC struct {
	*baseC
}

//广告列表
func (this *adC) List(ctx *web.Context) {
	dps.AdvertisementService.GetAdvertisement(this.GetPartnerId(ctx),0)
	ctx.App.Template().Execute(ctx.ResponseWriter, gof.TemplateDataMap{
	}, "views/partner/ad/ad_list.html")
}

// 修改广告
func (this *adC) Edit(ctx *web.Context) {
	partnerId := this.GetPartnerId(ctx)
	form := ctx.Request.URL.Query()
	id, _ := strconv.Atoi(form.Get("id"))
	e := dps.AdvertisementService.GetAdvertisement(partnerId,id)

	js, _ := json.Marshal(e)

	ctx.App.Template().Execute(ctx.ResponseWriter,
		gof.TemplateDataMap{
			"entity": template.JS(js),
		},
		"views/partner/ad/ad_edit.html")
}

// 保存广告
func (this *adC) Create(ctx *web.Context) {
	e := content.ValuePage{
		Enabled:1,
	}

	js, _ := json.Marshal(e)

	ctx.App.Template().Execute(ctx.ResponseWriter,
		gof.TemplateDataMap{
			"entity": template.JS(js),
		},
		"views/partner/ad/ad_edit.html")
}

func (this *adC) SaveAd_post(ctx *web.Context) {
	partnerId := this.GetPartnerId(ctx)
	r := ctx.Request
	r.ParseForm()

	var result gof.Message

	e := content.ValuePage{}
	web.ParseFormToEntity(r.Form, &e)

	//更新
	e.UpdateTime = time.Now().Unix()
	e.PartnerId = partnerId

	id, err := dps.ContentService.SavePage(partnerId, &e)

	if err != nil {
		result.Message = err.Error()
	} else {
		result.Result = true
		result.Data = id
	}
	this.ResultOutput(ctx,result)
}
<commit_msg>ad manager<commit_after>/**
 * Copyright 2014 @ S1N1 Team.
 * name :
 * author : jarryliu
 * date : 2014-02-05 21:53
 * description :
 * history :
 */
package partner

import (
	"encoding/json"
	"github.com/atnet/gof"
	"github.com/atnet/gof/web"
	"github.com/atnet/gof/web/mvc"
	"go2o/src/core/service/dps"
	"html/template"
	"time"
	"strconv"
	"go2o/src/core/domain/interface/content"
)

var _ mvc.Filter = new(adC)

// 广告控制器
type adC struct {
	*baseC
}

//广告列表
func (this *adC) List(ctx *web.Context) {
	dps.AdvertisementService.GetAdvertisement(this.GetPartnerId(ctx),0)
	ctx.App.Template().Execute(ctx.ResponseWriter, gof.TemplateDataMap{
	}, "views/partner/ad/ad_list.html")
}

// 广告控制台
func (this *adC) Ad_ctrl(ctx *web.Context) {
	form := ctx.Request.URL.Query()
	id, _ := strconv.Atoi(form.Get("id"))

	ctx.App.Template().Execute(ctx.ResponseWriter, gof.TemplateDataMap{
		"id":id,
	}, "views/partner/ad/ad_ctrl.html")
}

// 修改广告
func (this *adC) Edit(ctx *web.Context) {
	partnerId := this.GetPartnerId(ctx)
	form := ctx.Request.URL.Query()
	id, _ := strconv.Atoi(form.Get("id"))
	e := dps.AdvertisementService.GetAdvertisement(partnerId,id)

	js, _ := json.Marshal(e)

	ctx.App.Template().Execute(ctx.ResponseWriter,
		gof.TemplateDataMap{
			"entity": template.JS(js),
		},
		"views/partner/ad/ad_edit.html")
}

// 保存广告
func (this *adC) Create(ctx *web.Context) {
	e := content.ValuePage{
		Enabled:1,
	}

	js, _ := json.Marshal(e)

	ctx.App.Template().Execute(ctx.ResponseWriter,
		gof.TemplateDataMap{
			"entity": template.JS(js),
		},
		"views/partner/ad/ad_edit.html")
}

func (this *adC) SaveAd_post(ctx *web.Context) {
	partnerId := this.GetPartnerId(ctx)
	r := ctx.Request
	r.ParseForm()

	var result gof.Message

	e := content.ValuePage{}
	web.ParseFormToEntity(r.Form, &e)

	//更新
	e.UpdateTime = time.Now().Unix()
	e.PartnerId = partnerId

	id, err := dps.ContentService.SavePage(partnerId, &e)

	if err != nil {
		result.Message = err.Error()
	} else {
		result.Result = true
		result.Data = id
	}
	this.ResultOutput(ctx,result)
}
<|endoftext|>


<commit_before>package flagparser

import "fmt"
import "github.com/hlandau/degoutils/configurable"

func name(c configurable.Configurable) (name string, ok bool) {
	v, ok := c.(interface {
		CfName() string
	})
	if !ok {
		return
	}

	return v.CfName(), true
}

func usageSummaryLine(c configurable.Configurable) (s string, ok bool) {
	v, ok := c.(interface {
		CfUsageSummaryLine() string
	})
	if !ok {
		return
	}

	return v.CfUsageSummaryLine(), true
}

func defaultValue(c configurable.Configurable) (s interface{}, ok bool) {
	v, ok := c.(interface {
		CfDefaultValue() interface{}
	})
	if !ok {
		return
	}

	return v.CfDefaultValue(), true
}

func defaultValueString(c configurable.Configurable) string {
	s, ok := defaultValue(c)
	if !ok {
		return ""
	}

	return fmt.Sprintf("%v", s)
}

var errNotSupported = fmt.Errorf("not supported")

func printFlagUsage(c configurable.Configurable) error {
	name, ok := name(c)
	if !ok {
		return errNotSupported
	}

	_, ok = c.(interface {
		CfSetValue(v interface{}) error
	})
	if !ok {
		return errNotSupported
	}

	usage, _ := usageSummaryLine(c)
	defValue := defaultValueString(c)
	col1 := fmt.Sprintf("  -%s=%s  ", name, defValue)

	fmt.Printf("%-40s%s\n", col1, usage)

	return nil
}

func processConfigurable(c configurable.Configurable) error {
	err := printFlagUsage(c)
	if err != nil {
		fmt.Printf("%v\n", c)
	}

	for _, ch := range c.CfChildren() {
		processConfigurable(ch)
	}

	return nil
}

func ParseFlags() {
	configurable.Visit(func(c configurable.Configurable) error {
		return processConfigurable(c)
	})
}
<commit_msg>Configurable<commit_after><|endoftext|>


<commit_before>package binding

import (
	"encoding/json"
	"encoding/xml"
	"errors"
	"net/http"
	"reflect"
	"strconv"
	"strings"
)

type (
	Binding interface {
		Bind(*http.Request, interface{}) error
	}

	// JSON binding
	jsonBinding struct{}

	// XML binding
	xmlBinding struct{}

	// // form binding
	formBinding struct{}
)

var (
	JSON = jsonBinding{}
	XML  = xmlBinding{}
	Form = formBinding{} // todo
)

func (_ jsonBinding) Bind(req *http.Request, obj interface{}) error {
	decoder := json.NewDecoder(req.Body)
	if err := decoder.Decode(obj); err == nil {
		return Validate(obj)
	} else {
		return err
	}
}

func (_ xmlBinding) Bind(req *http.Request, obj interface{}) error {
	decoder := xml.NewDecoder(req.Body)
	if err := decoder.Decode(obj); err == nil {
		return Validate(obj)
	} else {
		return err
	}
}

func (_ formBinding) Bind(req *http.Request, obj interface{}) error {
	if err := req.ParseForm(); err != nil {
		return err
	}
	if err := mapForm(obj, req.Form); err != nil {
		return err
	}
	return Validate(obj)
}

func mapForm(ptr interface{}, form map[string][]string) error {
	typ := reflect.TypeOf(ptr).Elem()
	formStruct := reflect.ValueOf(ptr).Elem()
	for i := 0; i < typ.NumField(); i++ {
		typeField := typ.Field(i)
		if inputFieldName := typeField.Tag.Get("form"); inputFieldName != "" {
			structField := formStruct.Field(i)
			if !structField.CanSet() {
				continue
			}

			inputValue, exists := form[inputFieldName]
			if !exists {
				continue
			}
			numElems := len(inputValue)
			if structField.Kind() == reflect.Slice && numElems > 0 {
				sliceOf := structField.Type().Elem().Kind()
				slice := reflect.MakeSlice(structField.Type(), numElems, numElems)
				for i := 0; i < numElems; i++ {
					if err := setWithProperType(sliceOf, inputValue[i], slice.Index(i)); err != nil {
						return err
					}
				}
				formStruct.Elem().Field(i).Set(slice)
			} else {
				if err := setWithProperType(typeField.Type.Kind(), inputValue[0], structField); err != nil {
					return err
				}
			}
		}
	}
	return nil
}

func setWithProperType(valueKind reflect.Kind, val string, structField reflect.Value) error {
	switch valueKind {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		if val == "" {
			val = "0"
		}
		intVal, err := strconv.Atoi(val)
		if err != nil {
			return err
		} else {
			structField.SetInt(int64(intVal))
		}
	case reflect.Bool:
		if val == "" {
			val = "false"
		}
		boolVal, err := strconv.ParseBool(val)
		if err != nil {
			return err
		} else {
			structField.SetBool(boolVal)
		}
	case reflect.Float32:
		if val == "" {
			val = "0.0"
		}
		floatVal, err := strconv.ParseFloat(val, 32)
		if err != nil {
			return err
		} else {
			structField.SetFloat(floatVal)
		}
	case reflect.Float64:
		if val == "" {
			val = "0.0"
		}
		floatVal, err := strconv.ParseFloat(val, 64)
		if err != nil {
			return err
		} else {
			structField.SetFloat(floatVal)
		}
	case reflect.String:
		structField.SetString(val)
	}
	return nil
}

// Don't pass in pointers to bind to. Can lead to bugs. See:
// https://github.com/codegangsta/martini-contrib/issues/40
// https://github.com/codegangsta/martini-contrib/pull/34#issuecomment-29683659
func ensureNotPointer(obj interface{}) {
	if reflect.TypeOf(obj).Kind() == reflect.Ptr {
		panic("Pointers are not accepted as binding models")
	}
}

func Validate(obj interface{}) error {

	typ := reflect.TypeOf(obj)
	val := reflect.ValueOf(obj)

	if typ.Kind() == reflect.Ptr {
		typ = typ.Elem()
		val = val.Elem()
	}

	for i := 0; i < typ.NumField(); i++ {
		field := typ.Field(i)
		fieldValue := val.Field(i).Interface()
		zero := reflect.Zero(field.Type).Interface()

		// Validate nested and embedded structs (if pointer, only do so if not nil)
		if field.Type.Kind() == reflect.Struct ||
			(field.Type.Kind() == reflect.Ptr && !reflect.DeepEqual(zero, fieldValue)) {
			if err := Validate(fieldValue); err != nil {
				return err
			}
		}

		if strings.Index(field.Tag.Get("binding"), "required") > -1 {
			if reflect.DeepEqual(zero, fieldValue) {
				name := field.Name
				if j := field.Tag.Get("json"); j != "" {
					name = j
				} else if f := field.Tag.Get("form"); f != "" {
					name = f
				}
				return errors.New("Required " + name)
			}
		}
	}
	return nil
}
<commit_msg>Fixes "Can't unmarshal JSON array with #63"<commit_after>package binding

import (
	"encoding/json"
	"encoding/xml"
	"errors"
	"net/http"
	"reflect"
	"strconv"
	"strings"
)

type (
	Binding interface {
		Bind(*http.Request, interface{}) error
	}

	// JSON binding
	jsonBinding struct{}

	// XML binding
	xmlBinding struct{}

	// // form binding
	formBinding struct{}
)

var (
	JSON = jsonBinding{}
	XML  = xmlBinding{}
	Form = formBinding{} // todo
)

func (_ jsonBinding) Bind(req *http.Request, obj interface{}) error {
	decoder := json.NewDecoder(req.Body)
	if err := decoder.Decode(obj); err == nil {
		return Validate(obj)
	} else {
		return err
	}
}

func (_ xmlBinding) Bind(req *http.Request, obj interface{}) error {
	decoder := xml.NewDecoder(req.Body)
	if err := decoder.Decode(obj); err == nil {
		return Validate(obj)
	} else {
		return err
	}
}

func (_ formBinding) Bind(req *http.Request, obj interface{}) error {
	if err := req.ParseForm(); err != nil {
		return err
	}
	if err := mapForm(obj, req.Form); err != nil {
		return err
	}
	return Validate(obj)
}

func mapForm(ptr interface{}, form map[string][]string) error {
	typ := reflect.TypeOf(ptr).Elem()
	formStruct := reflect.ValueOf(ptr).Elem()
	for i := 0; i < typ.NumField(); i++ {
		typeField := typ.Field(i)
		if inputFieldName := typeField.Tag.Get("form"); inputFieldName != "" {
			structField := formStruct.Field(i)
			if !structField.CanSet() {
				continue
			}

			inputValue, exists := form[inputFieldName]
			if !exists {
				continue
			}
			numElems := len(inputValue)
			if structField.Kind() == reflect.Slice && numElems > 0 {
				sliceOf := structField.Type().Elem().Kind()
				slice := reflect.MakeSlice(structField.Type(), numElems, numElems)
				for i := 0; i < numElems; i++ {
					if err := setWithProperType(sliceOf, inputValue[i], slice.Index(i)); err != nil {
						return err
					}
				}
				formStruct.Elem().Field(i).Set(slice)
			} else {
				if err := setWithProperType(typeField.Type.Kind(), inputValue[0], structField); err != nil {
					return err
				}
			}
		}
	}
	return nil
}

func setWithProperType(valueKind reflect.Kind, val string, structField reflect.Value) error {
	switch valueKind {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		if val == "" {
			val = "0"
		}
		intVal, err := strconv.Atoi(val)
		if err != nil {
			return err
		} else {
			structField.SetInt(int64(intVal))
		}
	case reflect.Bool:
		if val == "" {
			val = "false"
		}
		boolVal, err := strconv.ParseBool(val)
		if err != nil {
			return err
		} else {
			structField.SetBool(boolVal)
		}
	case reflect.Float32:
		if val == "" {
			val = "0.0"
		}
		floatVal, err := strconv.ParseFloat(val, 32)
		if err != nil {
			return err
		} else {
			structField.SetFloat(floatVal)
		}
	case reflect.Float64:
		if val == "" {
			val = "0.0"
		}
		floatVal, err := strconv.ParseFloat(val, 64)
		if err != nil {
			return err
		} else {
			structField.SetFloat(floatVal)
		}
	case reflect.String:
		structField.SetString(val)
	}
	return nil
}

// Don't pass in pointers to bind to. Can lead to bugs. See:
// https://github.com/codegangsta/martini-contrib/issues/40
// https://github.com/codegangsta/martini-contrib/pull/34#issuecomment-29683659
func ensureNotPointer(obj interface{}) {
	if reflect.TypeOf(obj).Kind() == reflect.Ptr {
		panic("Pointers are not accepted as binding models")
	}
}

func Validate(obj interface{}) error {
	typ := reflect.TypeOf(obj)
	val := reflect.ValueOf(obj)

	if typ.Kind() == reflect.Ptr {
		typ = typ.Elem()
		val = val.Elem()
	}

	switch typ.Kind() {
	case reflect.Struct:
		for i := 0; i < typ.NumField(); i++ {
			field := typ.Field(i)

			// Allow ignored fields in the struct
			if field.Tag.Get("form") == "-" {
				continue
			}

			fieldValue := val.Field(i).Interface()
			zero := reflect.Zero(field.Type).Interface()

			if strings.Index(field.Tag.Get("binding"), "required") > -1 {
				fieldType := field.Type.Kind()
				if fieldType == reflect.Struct {
					err := Validate(fieldValue)
					if err != nil {
						return err
					}
				} else if reflect.DeepEqual(zero, fieldValue) {
					return errors.New("Required " + field.Name)
				} else if fieldType == reflect.Slice && field.Type.Elem().Kind() == reflect.Struct {
					err := Validate(fieldValue)
					if err != nil {
						return err
					}
				}
			}
		}
	case reflect.Slice:
		for i := 0; i < val.Len(); i++ {
			fieldValue := val.Index(i).Interface()
			err := Validate(fieldValue)
			if err != nil {
				return err
			}
		}
	default:
		return nil
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/codegangsta/cli"
	"github.com/mktmpio/cli/commands"
	"os"
)

var version string

func main() {
	app := cli.NewApp()
	app.Version = version
	app.Name = "mktmpio"
	app.Usage = "create, destroy, and manage mktmpio instances"
	// Make the default action a remote shell, so 'mktmpio redis' always gives a
	// remote Redis shell
	app.Action = commands.ShellCommand.Action
	app.Commands = []cli.Command{
		commands.ShellCommand,
	}
	app.Run(os.Args)
}
<commit_msg>Set a default version for non-versioned builds<commit_after>package main

import (
	"github.com/codegangsta/cli"
	"github.com/mktmpio/cli/commands"
	"os"
)

var version = "HEAD"

func main() {
	app := cli.NewApp()
	app.Version = version
	app.Name = "mktmpio"
	app.Usage = "create, destroy, and manage mktmpio instances"
	// Make the default action a remote shell, so 'mktmpio redis' always gives a
	// remote Redis shell
	app.Action = commands.ShellCommand.Action
	app.Commands = []cli.Command{
		commands.ShellCommand,
	}
	app.Run(os.Args)
}
<|endoftext|>