github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/third_party/code.google.com/p/go-charset/charset/local.go (about)

     1  package charset
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"os"
     7  	"sync"
     8  )
     9  
    10  var (
    11  	readLocalCharsetsOnce sync.Once
    12  	localCharsets         = make(map[string]*localCharset)
    13  )
    14  
    15  type localCharset struct {
    16  	Charset
    17  	arg string
    18  	*class
    19  }
    20  
    21  // A class of character sets.
    22  // Each class can be instantiated with an argument specified in the config file.
    23  // Many character sets can use a single class.
    24  type class struct {
    25  	from, to func(arg string) (Translator, error)
    26  }
    27  
    28  // The set of classes, indexed by class name.
    29  var classes = make(map[string]*class)
    30  
    31  func registerClass(charset string, from, to func(arg string) (Translator, error)) {
    32  	classes[charset] = &class{from, to}
    33  }
    34  
    35  type localFactory struct{}
    36  
    37  func (f localFactory) TranslatorFrom(name string) (Translator, error) {
    38  	f.init()
    39  	name = NormalizedName(name)
    40  	cs := localCharsets[name]
    41  	if cs == nil {
    42  		return nil, fmt.Errorf("character set %q not found", name)
    43  	}
    44  	if cs.from == nil {
    45  		return nil, fmt.Errorf("cannot translate from %q", name)
    46  	}
    47  	return cs.from(cs.arg)
    48  }
    49  
    50  func (f localFactory) TranslatorTo(name string) (Translator, error) {
    51  	f.init()
    52  	name = NormalizedName(name)
    53  	cs := localCharsets[name]
    54  	if cs == nil {
    55  		return nil, fmt.Errorf("character set %q not found", name)
    56  	}
    57  	if cs.to == nil {
    58  		return nil, fmt.Errorf("cannot translate to %q", name)
    59  	}
    60  	return cs.to(cs.arg)
    61  }
    62  
    63  func (f localFactory) Names() []string {
    64  	f.init()
    65  	var names []string
    66  	for name, cs := range localCharsets {
    67  		// add names only for non-aliases.
    68  		if localCharsets[cs.Name] == cs {
    69  			names = append(names, name)
    70  		}
    71  	}
    72  	return names
    73  }
    74  
    75  func (f localFactory) Info(name string) *Charset {
    76  	f.init()
    77  	lcs := localCharsets[NormalizedName(name)]
    78  	if lcs == nil {
    79  		return nil
    80  	}
    81  	// copy the charset info so that callers can't mess with it.
    82  	cs := lcs.Charset
    83  	return &cs
    84  }
    85  
    86  func (f localFactory) init() {
    87  	readLocalCharsetsOnce.Do(readLocalCharsets)
    88  }
    89  
    90  // charsetEntry is the data structure for one entry in the JSON config file.
    91  // If Alias is non-empty, it should be the canonical name of another
    92  // character set; otherwise Class should be the name
    93  // of an entry in classes, and Arg is the argument for
    94  // instantiating it.
    95  type charsetEntry struct {
    96  	Aliases []string
    97  	Desc    string
    98  	Class   string
    99  	Arg     string
   100  }
   101  
   102  // readCharsets reads the JSON config file.
   103  // It's done once only, when first needed.
   104  func readLocalCharsets() {
   105  	csdata, err := readFile("charsets.json")
   106  	if err != nil {
   107  		fmt.Fprintf(os.Stderr, "charset: cannot open \"charsets.json\": %v\n", err)
   108  		return
   109  	}
   110  
   111  	var entries map[string]charsetEntry
   112  	err = json.Unmarshal(csdata, &entries)
   113  	if err != nil {
   114  		fmt.Fprintf(os.Stderr, "charset: cannot decode config file: %v\n", err)
   115  	}
   116  	for name, e := range entries {
   117  		class := classes[e.Class]
   118  		if class == nil {
   119  			continue
   120  		}
   121  		name = NormalizedName(name)
   122  		for i, a := range e.Aliases {
   123  			e.Aliases[i] = NormalizedName(a)
   124  		}
   125  		cs := &localCharset{
   126  			Charset: Charset{
   127  				Name:    name,
   128  				Aliases: e.Aliases,
   129  				Desc:    e.Desc,
   130  				NoFrom:  class.from == nil,
   131  				NoTo:    class.to == nil,
   132  			},
   133  			arg:   e.Arg,
   134  			class: class,
   135  		}
   136  		localCharsets[cs.Name] = cs
   137  		for _, a := range cs.Aliases {
   138  			localCharsets[a] = cs
   139  		}
   140  	}
   141  }
   142  
   143  // A general cache store that local character set translators
   144  // can use for persistent storage of data.
   145  var (
   146  	cacheMutex sync.Mutex
   147  	cacheStore = make(map[interface{}]interface{})
   148  )
   149  
   150  func cache(key interface{}, f func() (interface{}, error)) (interface{}, error) {
   151  	cacheMutex.Lock()
   152  	defer cacheMutex.Unlock()
   153  	if x := cacheStore[key]; x != nil {
   154  		return x, nil
   155  	}
   156  	x, err := f()
   157  	if err != nil {
   158  		return nil, err
   159  	}
   160  	cacheStore[key] = x
   161  	return x, err
   162  }