• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 package com.android.xsdc;
18 
19 import java.io.File;
20 import java.io.FileInputStream;
21 import java.io.IOException;
22 import java.io.PrintWriter;
23 import java.nio.file.Paths;
24 import java.util.ArrayList;
25 import java.util.List;
26 import java.util.stream.Collectors;
27 
28 import static java.lang.System.exit;
29 
30 import com.android.xsdc.java.JavaCodeGenerator;
31 import com.android.xsdc.cpp.CppCodeGenerator;
32 
33 import org.apache.commons.cli.CommandLine;
34 import org.apache.commons.cli.CommandLineParser;
35 import org.apache.commons.cli.GnuParser;
36 import org.apache.commons.cli.HelpFormatter;
37 import org.apache.commons.cli.Option;
38 import org.apache.commons.cli.OptionBuilder;
39 import org.apache.commons.cli.OptionGroup;
40 import org.apache.commons.cli.Options;
41 import org.apache.commons.cli.ParseException;
42 
43 import javax.xml.parsers.SAXParser;
44 import javax.xml.parsers.SAXParserFactory;
45 
46 public class Main {
main(String[] args)47     public static void main(String[] args) throws Exception {
48         Options options = new Options();
49         options.addOption(OptionBuilder
50                 .withLongOpt("package")
51                 .hasArgs(1)
52                 .withDescription("Package name of the generated java file. " +
53                         "file name of generated cpp file and header")
54                 .create("p"));
55         options.addOption(OptionBuilder
56                 .withLongOpt("outDir")
57                 .hasArgs(1)
58                 .withDescription("Out Directory")
59                 .create("o"));
60         options.addOption(OptionBuilder
61                 .withLongOpt("java")
62                 .hasArgs(0)
63                 .withDescription("Generate Java code.")
64                 .create("j"));
65         options.addOption(OptionBuilder
66                 .withLongOpt("cpp")
67                 .hasArgs(0)
68                 .withDescription("Generate Cpp code.")
69                 .create("c"));
70         options.addOption(OptionBuilder
71                 .withLongOpt("writer")
72                 .hasArgs(0)
73                 .withDescription("Generate Writer code.")
74                 .create("w"));
75         options.addOption(OptionBuilder
76                 .withLongOpt("nullability")
77                 .hasArgs(0)
78                 .withDescription("Add @NonNull or @Nullable annotation to generated java code.")
79                 .create("n"));
80         options.addOption(OptionBuilder
81                 .withLongOpt("genHas")
82                 .hasArgs(0)
83                 .withDescription("Generate public hasX() method")
84                 .create("g"));
85         options.addOption(OptionBuilder
86                 .withLongOpt("booleanGetter")
87                 .hasArgs(0)
88                 .withDescription("Generate isX() for boolean element or attribute.")
89                 .create("b"));
90         options.addOption(OptionBuilder
91                 .withLongOpt("tinyxml")
92                 .hasArgs(0)
93                 .withDescription("Generate code that uses libtinyxml2 instead of libxml2."
94                     + " Smaller binaries, but does not support XInclude substitution, "
95                     + " or ENTITY_REFs.")
96                 .create("t"));
97         Option genEnumsOnly = OptionBuilder
98                 .withLongOpt("genEnumsOnly")
99                 .hasArgs(0)
100                 .withDescription("Only generate enum converters in Cpp code.")
101                 .create("e");
102         options.addOption(genEnumsOnly);
103         Option genParserOnly = OptionBuilder
104                 .withLongOpt("genParserOnly")
105                 .hasArgs(0)
106                 .withDescription("Only generate XML parser in Cpp code.")
107                 .create("x");
108         options.addOption(genParserOnly);
109         Option genDepFile = OptionBuilder
110                 .withLongOpt("depfile")
111                 .hasArgs(1)
112                 .withDescription("Generate depfile for ninja.")
113                 .create("d");
114         options.addOption(genDepFile);
115         options.addOption(OptionBuilder
116                 .withLongOpt("root")
117                 .hasArgs(1)
118                 .withDescription("Root element.")
119                 .create("r"));
120 
121         // "Only generate enums" and "Only generate parser" options are mutually exclusive.
122         OptionGroup genOnlyGroup = new OptionGroup();
123         genOnlyGroup.setRequired(false);
124         genOnlyGroup.addOption(genEnumsOnly);
125         genOnlyGroup.addOption(genParserOnly);
126         options.addOptionGroup(genOnlyGroup);
127 
128         CommandLineParser CommandParser = new GnuParser();
129         CommandLine cmd;
130 
131         try {
132             cmd = CommandParser.parse(options, args);
133         } catch (ParseException e) {
134             System.err.println(e.getMessage());
135             help(options);
136             return;
137         }
138 
139         String packageName = cmd.getOptionValue('p', null);
140         String outDir = cmd.getOptionValue('o', null);
141         boolean writer = cmd.hasOption('w');
142         boolean nullability = cmd.hasOption('n');
143         boolean genHas = cmd.hasOption('g');
144         boolean enumsOnly = cmd.hasOption('e');
145         boolean parserOnly = cmd.hasOption('x');
146         boolean booleanGetter = cmd.hasOption('b');
147         boolean useTinyXml = cmd.hasOption('t');
148         String depFile = cmd.getOptionValue('d', null);
149         String[] rootElements = cmd.getOptionValues('r');
150 
151         if (cmd.getArgs().length != 1 || packageName == null) {
152             System.err.println("Error: no xsd files or package name");
153             help(options);
154         }
155 
156         if (outDir == null) {
157             outDir = ".";
158         }
159 
160         String xsdFile = cmd.getArgs()[0];
161         List<String> included = new ArrayList<>();
162         included.add(xsdFile);
163         XmlSchema xmlSchema = parse(xsdFile, included);
164 
165         // When -r (root element) is specified, then validate if it's defined in schema.
166         if (rootElements != null) {
167             for (String rootElement : rootElements) {
168                 if (!xmlSchema.getElementMap().containsKey(rootElement)) {
169                     System.err.println("Invalid root element(-r): " + rootElement);
170                     System.exit(1);
171                 }
172             }
173         } else {
174             // Raise an error when -r/--root is not specified but there's more than one
175             // candidate root elements (top-level elements).
176             if (xmlSchema.getElementMap().size() > 1) {
177                 System.err.println("Missing -r/--root option: please specify the names of root "
178                         + "elements. In Android.bp, use 'root_elements' property to set root "
179                         + "elements. Possible root elements are: "
180                         + xmlSchema.getElementMap().keySet().stream().map(s -> "\"" + s + "\"")
181                           .collect(Collectors.joining(", ")));
182                 System.exit(1);
183             }
184         }
185 
186         if (cmd.hasOption('j')) {
187             File packageDir = new File(Paths.get(outDir, packageName.replace(".", "/")).toString());
188             packageDir.mkdirs();
189             FileSystem fs = new FileSystem(packageDir);
190             JavaCodeGenerator javaCodeGenerator =
191                     new JavaCodeGenerator(xmlSchema, packageName, writer, nullability, genHas,
192                                           booleanGetter, rootElements);
193             javaCodeGenerator.print(fs);
194         } else if (cmd.hasOption('c')) {
195             File includeDir = new File(Paths.get(outDir, "include").toString());
196             includeDir.mkdirs();
197             FileSystem fs = new FileSystem(new File(outDir));
198             int generators = enumsOnly ? CppCodeGenerator.GENERATE_ENUMS :
199                     (parserOnly ? CppCodeGenerator.GENERATE_PARSER :
200                             CppCodeGenerator.GENERATE_ENUMS | CppCodeGenerator.GENERATE_PARSER);
201             CppCodeGenerator cppCodeGenerator =
202                     new CppCodeGenerator(xmlSchema, packageName, writer, generators,
203                             booleanGetter, useTinyXml, rootElements);
204             cppCodeGenerator.print(fs);
205         }
206 
207         if (depFile != null) {
208             writeDepFile(depFile, included);
209         }
210     }
211 
parse(String xsdFile, List<String> included)212     private static XmlSchema parse(String xsdFile, List<String> included) throws Exception {
213         XmlSchema xmlSchema;
214         try (FileInputStream in = new FileInputStream(xsdFile)) {
215             SAXParserFactory factory = SAXParserFactory.newInstance();
216             factory.setNamespaceAware(true);
217             SAXParser parser = factory.newSAXParser();
218             XsdHandler xsdHandler = new XsdHandler();
219             parser.parse(in, xsdHandler);
220             xmlSchema = xsdHandler.getSchema();
221         }
222         for (String file : xmlSchema.getIncludeList()) {
223             String filePath = Paths.get(xsdFile).resolveSibling(file).toString();
224             included.add(filePath);
225             XmlSchema temp = parse(filePath, included);
226             xmlSchema.include(temp);
227         }
228         return xmlSchema;
229     }
230 
writeDepFile(String depFile, List<String> files)231     private static void writeDepFile(String depFile, List<String> files)
232             throws IOException {
233         try (PrintWriter out = new PrintWriter(new File(depFile))) {
234             out.println(String.format("  %s", String.join(" \\\n  ", files)));
235         }
236     }
237 
help(Options options)238     private static void help(Options options) {
239         new HelpFormatter().printHelp(
240                 "xsdc path/to/xsd_file.xsd","", options, null, true);
241         System.exit(1);
242     }
243 }
244