xref: /aosp_15_r20/frameworks/layoutlib/validator/src/ResourceConverter.java (revision fc3927be90a325f95c74a9043993a80ef388dc46)
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.nio.charset.StandardCharsets;
26 import java.util.LinkedHashMap;
27 import java.util.Map;
28 import java.util.Map.Entry;
29 
30 import javax.xml.parsers.DocumentBuilder;
31 import javax.xml.parsers.DocumentBuilderFactory;
32 
33 
34 public class ResourceConverter {
35 
36     /**
37      * Convert the Android strings.xml into generic Java strings.properties.
38      */
main(String[] args)39     public static void main(String[] args) throws Exception {
40         System.out.println("Parsing input...");
41         Map<String, String> map = loadStrings("./validator/resources/strings.xml");
42         System.out.println("Writing to output...");
43         writeStrings(map, "./validator/resources/strings.properties");
44         System.out.println("Finished converting.");
45     }
46 
47     /**
48      * Writes <name, value> pair to outputPath.
49      */
writeStrings(Map<String, String> map, String outputPath)50     private static void writeStrings(Map<String, String> map, String outputPath) throws Exception {
51         File output = new File(outputPath);
52         output.createNewFile();
53         try (FileWriter writer = new FileWriter(output, StandardCharsets.UTF_8)) {
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         }
62     }
63 
64     /**
65      * Very hacky parser for Android-understood-values.xml. It parses <string> </string>
66      * tags, and retrieve the name and the value associated.
67      *
68      * @param path to .xml containing android strings
69      * @return Map containing name and values.
70      */
71     @Nullable
loadStrings(String path)72     private static Map<String, String> loadStrings(String path)
73             throws Exception {
74         // Use ordered map to minimize changes to strings.properties in git.
75         Map<String, String> toReturn = new LinkedHashMap<>();
76 
77         File file = new File(path);
78         if (!file.exists()) {
79             System.err.println("The input file "+ path + " does not exist. Terminating.");
80             return toReturn;
81         }
82 
83         DocumentBuilder documentBuilder = DocumentBuilderFactory
84                 .newInstance().newDocumentBuilder();
85         Document document = documentBuilder.parse(file);
86         NodeList nodeList = document.getElementsByTagName("string");
87         for (int i = 0; i < nodeList.getLength(); i++) {
88             Node node = nodeList.item(i);
89             String name = node.getAttributes().getNamedItem("name").getNodeValue();
90 
91             StringBuilder valueBuilder = new StringBuilder();
92             try {
93                 /*
94                   This is a very hacky way to bypass "ns1:g" tag in android's .xml.
95                   Ideally we'll read the tag from the parent and apply it here, but it being the
96                   deep node list I'm not currently sure how to parse it safely. Might need to look
97                   into IntelliJ PSI tree we have in Studio. But I didn't want to add unnecessary
98                   deps to LayoutLib.
99 
100                   It also means resource namespaces are rendered useless after conversion.
101                  */
102                 for (int j = 0; j < node.getChildNodes().getLength(); j++) {
103                     Node child = node.getChildNodes().item(j);
104                     String toAdd;
105                     if ("ns1:g".equals(child.getNodeName())) {
106                         toAdd = child.getFirstChild().getNodeValue();
107                     } else if ("xliff:g".equals(child.getNodeName())) {
108                         toAdd = child.getFirstChild().getNodeValue();
109                     } else {
110                         toAdd = child.getNodeValue();
111                     }
112                     // Replace all tab, newline and multi indentations.
113                     toAdd = toAdd.replaceAll("[\n\t]", "");
114                     toAdd = toAdd.replaceAll("[ ]+", " ");
115                     valueBuilder.append(toAdd);
116                 }
117                 String finalString = valueBuilder.toString().trim();
118                 toReturn.put(name, finalString);
119             } catch (Exception e) {
120                 e.printStackTrace();
121             }
122         }
123         return toReturn;
124     }
125 
getCopyRight()126     private static String getCopyRight() {
127         return """
128 
129                 #
130                 # Copyright (C) 2020 The Android Open Source Project
131                 #
132                 # Licensed under the Apache License, Version 2.0 (the "License");
133                 # you may not use this file except in compliance with the License.
134                 # You may obtain a copy of the License at
135                 #
136                 #      http://www.apache.org/licenses/LICENSE-2.0
137                 #
138                 # Unless required by applicable law or agreed to in writing, software
139                 # distributed under the License is distributed on an "AS IS" BASIS,
140                 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
141                 # See the License for the specific language governing permissions and
142                 # limitations under the License.
143                 #""";
144     }
145 }
146