• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 import com.android.tools.layoutlib.annotations.Nullable;
18 
19 import org.w3c.dom.Document;
20 import org.w3c.dom.Node;
21 import org.w3c.dom.NodeList;
22 
23 import java.io.File;
24 import java.io.FileWriter;
25 import java.util.LinkedHashMap;
26 import java.util.Map;
27 import java.util.Map.Entry;
28 
29 import javax.xml.parsers.DocumentBuilder;
30 import javax.xml.parsers.DocumentBuilderFactory;
31 
32 
33 public class ResourceConverter {
34 
35     /**
36      * Convert the Android strings.xml into generic Java strings.properties.
37      */
main(String[] args)38     public static void main(String[] args) throws Exception {
39         System.out.println("Parsing input...");
40         Map<String, String> map = loadStrings("./validator/resources/values.xml");
41         System.out.println("Writing to output...");
42         writeStrings(map, "./validator/resources/strings.properties");
43         System.out.println("Finished converting.");
44     }
45 
46     /**
47      * Writes <name, value> pair to outputPath.
48      */
writeStrings(Map<String, String> map, String outputPath)49     private static void writeStrings(Map<String, String> map, String outputPath) throws Exception {
50         File output = new File(outputPath);
51         output.createNewFile();
52         FileWriter writer = new FileWriter(output);
53         try {
54             writer.write(getCopyRight());
55             writer.write("\n");
56             for (Entry<String, String> entry : map.entrySet()) {
57                 String name = entry.getKey();
58                 String value = entry.getValue();
59                 writer.write(name + " = " + value + "\n");
60             }
61         } finally {
62             writer.close();
63         }
64     }
65 
66     /**
67      * Very hacky parser for Android-understood-values.xml. It parses <string> </string>
68      * tags, and retrieve the name and the value associated.
69      *
70      * @param path to .xml containing android strings
71      * @return Map containing name and values.
72      */
73     @Nullable
loadStrings(String path)74     private static Map<String, String> loadStrings(String path)
75             throws Exception {
76         // Use ordered map to minimize changes to strings.properties in git.
77         Map<String, String> toReturn = new LinkedHashMap<>();
78 
79         File file = new File(path);
80         if (!file.exists()) {
81             System.err.println("The input file "+ path + " does not exist. Terminating.");
82             return toReturn;
83         }
84 
85         DocumentBuilder documentBuilder = DocumentBuilderFactory
86                 .newInstance().newDocumentBuilder();
87         Document document = documentBuilder.parse(file);
88         NodeList nodeList = document.getElementsByTagName("string");
89         for (int i = 0; i < nodeList.getLength(); i++) {
90             Node node = nodeList.item(i);
91             String name = node.getAttributes().getNamedItem("name").getNodeValue();
92 
93             StringBuilder valueBuilder = new StringBuilder();
94             /**
95              * This is a very hacky way to bypass "ns1:g" tag in android's .xml.
96              * Ideally we'll read the tag from the parent and apply it here, but it being the
97              * deep node list I'm not currently sure how to parse it safely. Might need to look
98              * into IntelliJ PSI tree we have in Studio. But I didn't want to add unnecessary deps
99              * to LayoutLib.
100              *
101              * It also means resource namespaces are rendered useless after conversion.
102              */
103             for (int j = 0; j < node.getChildNodes().getLength(); j++) {
104                 Node child = node.getChildNodes().item(j);
105                 if ("ns1:g".equals(child.getNodeName())) {
106                     valueBuilder.append(child.getFirstChild().getNodeValue());
107                 } else {
108                     valueBuilder.append(child.getNodeValue());
109                 }
110             }
111             toReturn.put(name, valueBuilder.toString());
112         }
113         return toReturn;
114     }
115 
getCopyRight()116     private static String getCopyRight() {
117         return "\n" + "#\n" + "# Copyright (C) 2020 The Android Open Source Project\n" + "#\n" +
118                 "# Licensed under the Apache License, Version 2.0 (the \"License\");\n" +
119                 "# you may not use this file except in compliance with the License.\n" +
120                 "# You may obtain a copy of the License at\n" + "#\n" +
121                 "#      http://www.apache.org/licenses/LICENSE-2.0\n" + "#\n" +
122                 "# Unless required by applicable law or agreed to in writing, software\n" +
123                 "# distributed under the License is distributed on an \"AS IS\" BASIS,\n" +
124                 "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" +
125                 "# See the License for the specific language governing permissions and\n" +
126                 "# limitations under the License.\n" + "#";
127     }
128 }
129