Home | History | Annotate | Download | only in localize
      1 #include "ValuesFile.h"
      2 
      3 #include "XMLHandler.h"
      4 
      5 #include <algorithm>
      6 #include <fcntl.h>
      7 #include <expat.h>
      8 #include <unistd.h>
      9 #include <errno.h>
     10 
     11 using namespace std;
     12 
     13 const char* const ANDROID_XMLNS = "http://schemas.android.com/apk/res/android";
     14 const char* const XLIFF_XMLNS = "urn:oasis:names:tc:xliff:document:1.2";
     15 
     16 const char *const NS_MAP[] = {
     17     "android", ANDROID_XMLNS,
     18     "xliff", XLIFF_XMLNS,
     19     NULL, NULL
     20 };
     21 
     22 const XMLNamespaceMap ANDROID_NAMESPACES(NS_MAP);
     23 
     24 
     25 // =====================================================================================
     26 class ArrayHandler : public XMLHandler
     27 {
     28 public:
     29     ArrayHandler(ValuesFile* vf, int version, const string& versionString, const string& id);
     30 
     31     virtual int OnStartElement(const SourcePos& pos, const string& ns, const string& name,
     32                                 const vector<XMLAttribute>& attrs, XMLHandler** next);
     33     virtual int OnText(const SourcePos& pos, const string& text);
     34     virtual int OnComment(const SourcePos& pos, const string& text);
     35 
     36 private:
     37     ValuesFile* m_vf;
     38     int m_version;
     39     int m_index;
     40     string m_versionString;
     41     string m_id;
     42     string m_comment;
     43 };
     44 
     45 ArrayHandler::ArrayHandler(ValuesFile* vf, int version, const string& versionString,
     46                             const string& id)
     47     :m_vf(vf),
     48      m_version(version),
     49      m_index(0),
     50      m_versionString(versionString),
     51      m_id(id)
     52 {
     53 }
     54 
     55 int
     56 ArrayHandler::OnStartElement(const SourcePos& pos, const string& ns, const string& name,
     57                                 const vector<XMLAttribute>& attrs, XMLHandler** next)
     58 {
     59     if (ns == "" && name == "item") {
     60         XMLNode* node = XMLNode::NewElement(pos, ns, name, attrs, XMLNode::EXACT);
     61         m_vf->AddString(StringResource(pos, pos.file, m_vf->GetConfiguration(),
     62                                             m_id, m_index, node, m_version, m_versionString,
     63                                             trim_string(m_comment)));
     64         *next = new NodeHandler(node, XMLNode::EXACT);
     65         m_index++;
     66         m_comment = "";
     67         return 0;
     68     } else {
     69         pos.Error("invalid <%s> element inside <array>\n", name.c_str());
     70         return 1;
     71     }
     72 }
     73 
     74 int
     75 ArrayHandler::OnText(const SourcePos& pos, const string& text)
     76 {
     77     return 0;
     78 }
     79 
     80 int
     81 ArrayHandler::OnComment(const SourcePos& pos, const string& text)
     82 {
     83     m_comment += text;
     84     return 0;
     85 }
     86 
     87 // =====================================================================================
     88 class ValuesHandler : public XMLHandler
     89 {
     90 public:
     91     ValuesHandler(ValuesFile* vf, int version, const string& versionString);
     92 
     93     virtual int OnStartElement(const SourcePos& pos, const string& ns, const string& name,
     94                                 const vector<XMLAttribute>& attrs, XMLHandler** next);
     95     virtual int OnText(const SourcePos& pos, const string& text);
     96     virtual int OnComment(const SourcePos& pos, const string& text);
     97 
     98 private:
     99     ValuesFile* m_vf;
    100     int m_version;
    101     string m_versionString;
    102     string m_comment;
    103 };
    104 
    105 ValuesHandler::ValuesHandler(ValuesFile* vf, int version, const string& versionString)
    106     :m_vf(vf),
    107      m_version(version),
    108      m_versionString(versionString)
    109 {
    110 }
    111 
    112 int
    113 ValuesHandler::OnStartElement(const SourcePos& pos, const string& ns, const string& name,
    114                                 const vector<XMLAttribute>& attrs, XMLHandler** next)
    115 {
    116     if (ns == "" && name == "string") {
    117         string id = XMLAttribute::Find(attrs, "", "name", "");
    118         XMLNode* node = XMLNode::NewElement(pos, ns, name, attrs, XMLNode::EXACT);
    119         m_vf->AddString(StringResource(pos, pos.file, m_vf->GetConfiguration(),
    120                                             id, -1, node, m_version, m_versionString,
    121                                             trim_string(m_comment)));
    122         *next = new NodeHandler(node, XMLNode::EXACT);
    123     }
    124     else if (ns == "" && name == "array") {
    125         string id = XMLAttribute::Find(attrs, "", "name", "");
    126         *next = new ArrayHandler(m_vf, m_version, m_versionString, id);
    127     }
    128     m_comment = "";
    129     return 0;
    130 }
    131 
    132 int
    133 ValuesHandler::OnText(const SourcePos& pos, const string& text)
    134 {
    135     return 0;
    136 }
    137 
    138 int
    139 ValuesHandler::OnComment(const SourcePos& pos, const string& text)
    140 {
    141     m_comment += text;
    142     return 0;
    143 }
    144 
    145 // =====================================================================================
    146 ValuesFile::ValuesFile(const Configuration& config)
    147     :m_config(config),
    148      m_strings(),
    149      m_arrays()
    150 {
    151 }
    152 
    153 ValuesFile::~ValuesFile()
    154 {
    155 }
    156 
    157 ValuesFile*
    158 ValuesFile::ParseFile(const string& filename, const Configuration& config,
    159                     int version, const string& versionString)
    160 {
    161     ValuesFile* result = new ValuesFile(config);
    162 
    163     TopElementHandler top("", "resources", new ValuesHandler(result, version, versionString));
    164     XMLHandler::ParseFile(filename, &top);
    165 
    166     return result;
    167 }
    168 
    169 ValuesFile*
    170 ValuesFile::ParseString(const string& filename, const string& text, const Configuration& config,
    171                     int version, const string& versionString)
    172 {
    173     ValuesFile* result = new ValuesFile(config);
    174 
    175     TopElementHandler top("", "resources", new ValuesHandler(result, version, versionString));
    176     XMLHandler::ParseString(filename, text, &top);
    177 
    178     return result;
    179 }
    180 
    181 const Configuration&
    182 ValuesFile::GetConfiguration() const
    183 {
    184     return m_config;
    185 }
    186 
    187 void
    188 ValuesFile::AddString(const StringResource& str)
    189 {
    190     if (str.index < 0) {
    191         m_strings.insert(str);
    192     } else {
    193         m_arrays[str.id].insert(str);
    194     }
    195 }
    196 
    197 set<StringResource>
    198 ValuesFile::GetStrings() const
    199 {
    200     set<StringResource> result = m_strings;
    201 
    202     for (map<string,set<StringResource> >::const_iterator it = m_arrays.begin();
    203             it != m_arrays.end(); it++) {
    204         result.insert(it->second.begin(), it->second.end());
    205     }
    206 
    207     return result;
    208 }
    209 
    210 XMLNode*
    211 ValuesFile::ToXMLNode() const
    212 {
    213     XMLNode* root;
    214 
    215     // <resources>
    216     {
    217         vector<XMLAttribute> attrs;
    218         ANDROID_NAMESPACES.AddToAttributes(&attrs);
    219         root = XMLNode::NewElement(GENERATED_POS, "", "resources", attrs, XMLNode::PRETTY);
    220     }
    221 
    222     // <array>
    223     for (map<string,set<StringResource> >::const_iterator it = m_arrays.begin();
    224             it != m_arrays.end(); it++) {
    225         vector<XMLAttribute> arrayAttrs;
    226         arrayAttrs.push_back(XMLAttribute("", "name", it->first));
    227         const set<StringResource>& items = it->second;
    228         XMLNode* arrayNode = XMLNode::NewElement(items.begin()->pos, "", "array", arrayAttrs,
    229                 XMLNode::PRETTY);
    230         root->EditChildren().push_back(arrayNode);
    231 
    232         // <item>
    233         for (set<StringResource>::const_iterator item = items.begin();
    234                 item != items.end(); item++) {
    235             XMLNode* itemNode = item->value->Clone();
    236             itemNode->SetName("", "item");
    237             itemNode->EditAttributes().clear();
    238             arrayNode->EditChildren().push_back(itemNode);
    239         }
    240     }
    241 
    242     // <string>
    243     for (set<StringResource>::const_iterator it=m_strings.begin(); it!=m_strings.end(); it++) {
    244         const StringResource& str = *it;
    245         vector<XMLAttribute> attrs;
    246         XMLNode* strNode = str.value->Clone();
    247         strNode->SetName("", "string");
    248         strNode->EditAttributes().clear();
    249         strNode->EditAttributes().push_back(XMLAttribute("", "name", str.id));
    250         root->EditChildren().push_back(strNode);
    251     }
    252 
    253     return root;
    254 }
    255 
    256 string
    257 ValuesFile::ToString() const
    258 {
    259     XMLNode* xml = ToXMLNode();
    260     string s = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
    261     s += xml->ToString(ANDROID_NAMESPACES);
    262     delete xml;
    263     s += '\n';
    264     return s;
    265 }
    266 
    267