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 }