2009-03-06 17:11:40 -07:00
|
|
|
// Copyright 2009 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package template
|
|
|
|
|
|
|
|
import (
|
|
|
|
"os";
|
|
|
|
"io";
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
type Template struct {
|
|
|
|
template []byte;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (T *Template) Init(filename string) *os.Error {
|
2009-03-11 13:51:10 -06:00
|
|
|
f, err0 := os.Open(filename, os.O_RDONLY, 0);
|
|
|
|
defer f.Close();
|
2009-03-06 17:11:40 -07:00
|
|
|
if err0 != nil {
|
|
|
|
return err0;
|
|
|
|
}
|
|
|
|
|
|
|
|
var buf io.ByteBuffer;
|
2009-03-11 13:51:10 -06:00
|
|
|
len, err1 := io.Copy(f, &buf);
|
2009-03-06 17:11:40 -07:00
|
|
|
if err1 == io.ErrEOF {
|
|
|
|
err1 = nil;
|
|
|
|
}
|
|
|
|
if err1 != nil {
|
|
|
|
return err1;
|
|
|
|
}
|
|
|
|
|
|
|
|
T.template = buf.Data();
|
2009-03-11 13:51:10 -06:00
|
|
|
|
2009-03-06 17:11:40 -07:00
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Returns true if buf starts with s, returns false otherwise.
|
2009-03-13 17:59:51 -06:00
|
|
|
//
|
2009-03-06 17:11:40 -07:00
|
|
|
func match(buf []byte, s string) bool {
|
|
|
|
if len(buf) < len(s) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
for i := 0; i < len(s); i++ {
|
|
|
|
if buf[i] != s[i] {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Find the position of string s in buf, starting at i.
|
|
|
|
// Returns a value < 0 if not found.
|
2009-03-13 17:59:51 -06:00
|
|
|
//
|
2009-03-06 17:11:40 -07:00
|
|
|
func find(buf []byte, s string, i int) int {
|
|
|
|
if s == "" {
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
L: for ; i + len(s) <= len(buf); i++ {
|
|
|
|
for k := 0; k < len(s); k++ {
|
|
|
|
if buf[i+k] != s[k] {
|
|
|
|
continue L;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type Substitution map [string] func()
|
|
|
|
|
|
|
|
func (T *Template) Apply(w io.Write, prefix string, subs Substitution) *os.Error {
|
|
|
|
i0 := 0; // position from which to write from the template
|
|
|
|
i1 := 0; // position from which to look for the next prefix
|
2009-03-11 13:51:10 -06:00
|
|
|
|
2009-03-06 17:11:40 -07:00
|
|
|
for {
|
|
|
|
// look for a prefix
|
|
|
|
i2 := find(T.template, prefix, i1); // position of prefix, if any
|
|
|
|
if i2 < 0 {
|
|
|
|
// no prefix found, we are done
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// we have a prefix, look for a matching key
|
|
|
|
i1 = i2 + len(prefix);
|
|
|
|
for key, action := range subs {
|
|
|
|
if match(T.template[i1 : len(T.template)], key) {
|
|
|
|
// found a match
|
2009-03-11 13:51:10 -06:00
|
|
|
i1 += len(key); // next search starting pos
|
2009-03-06 17:11:40 -07:00
|
|
|
len, err := w.Write(T.template[i0 : i2]); // TODO handle errors
|
|
|
|
i0 = i1; // skip placeholder
|
|
|
|
action();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// write the rest of the template
|
|
|
|
len, err := w.Write(T.template[i0 : len(T.template)]); // TODO handle errors
|
|
|
|
return err;
|
|
|
|
}
|
2009-03-13 17:59:51 -06:00
|
|
|
|
|
|
|
|
|
|
|
func NewTemplate(filename string) *Template {
|
|
|
|
t := new(Template);
|
|
|
|
if t.Init(filename) != nil {
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func NewTemplateOrDie(filename string) *Template {
|
|
|
|
t := NewTemplate(filename);
|
|
|
|
if t == nil {
|
2009-04-02 16:58:58 -06:00
|
|
|
panic("could not read template: " + filename);
|
2009-03-13 17:59:51 -06:00
|
|
|
}
|
|
|
|
return t;
|
|
|
|
}
|