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.nio.file.Paths;
22 
23 import static java.lang.System.exit;
24 
25 import com.android.xsdc.java.JavaCodeGenerator;
26 import com.android.xsdc.cpp.CppCodeGenerator;
27 
28 import org.apache.commons.cli.CommandLine;
29 import org.apache.commons.cli.CommandLineParser;
30 import org.apache.commons.cli.GnuParser;
31 import org.apache.commons.cli.HelpFormatter;
32 import org.apache.commons.cli.Option;
33 import org.apache.commons.cli.OptionBuilder;
34 import org.apache.commons.cli.OptionGroup;
35 import org.apache.commons.cli.Options;
36 import org.apache.commons.cli.ParseException;
37 
38 import javax.xml.parsers.SAXParser;
39 import javax.xml.parsers.SAXParserFactory;
40 
41 public class Main {
main(String[] args)42     public static void main(String[] args) throws Exception {
43         Options options = new Options();
44         options.addOption(OptionBuilder
45                 .withLongOpt("package")
46                 .hasArgs(1)
47                 .withDescription("Package name of the generated java file. " +
48                         "file name of generated cpp file and header")
49                 .create("p"));
50         options.addOption(OptionBuilder
51                 .withLongOpt("outDir")
52                 .hasArgs(1)
53                 .withDescription("Out Directory")
54                 .create("o"));
55         options.addOption(OptionBuilder
56                 .withLongOpt("java")
57                 .hasArgs(0)
58                 .withDescription("Generate Java code.")
59                 .create("j"));
60         options.addOption(OptionBuilder
61                 .withLongOpt("cpp")
62                 .hasArgs(0)
63                 .withDescription("Generate Cpp code.")
64                 .create("c"));
65         options.addOption(OptionBuilder
66                 .withLongOpt("writer")
67                 .hasArgs(0)
68                 .withDescription("Generate Writer code.")
69                 .create("w"));
70         options.addOption(OptionBuilder
71                 .withLongOpt("nullability")
72                 .hasArgs(0)
73                 .withDescription("Add @NonNull or @Nullable annotation to generated java code.")
74                 .create("n"));
75         options.addOption(OptionBuilder
76                 .withLongOpt("genHas")
77                 .hasArgs(0)
78                 .withDescription("Generate public hasX() method")
79                 .create("g"));
80         options.addOption(OptionBuilder
81                 .withLongOpt("booleanGetter")
82                 .hasArgs(0)
83                 .withDescription("Generate isX() for boolean element or attribute.")
84                 .create("b"));
85         Option genEnumsOnly = OptionBuilder
86                 .withLongOpt("genEnumsOnly")
87                 .hasArgs(0)
88                 .withDescription("Only generate enum converters in Cpp code.")
89                 .create("e");
90         options.addOption(genEnumsOnly);
91         Option genParserOnly = OptionBuilder
92                 .withLongOpt("genParserOnly")
93                 .hasArgs(0)
94                 .withDescription("Only generate XML parser in Cpp code.")
95                 .create("x");
96         options.addOption(genParserOnly);
97         // "Only generate enums" and "Only generate parser" options are mutually exclusive.
98         OptionGroup genOnlyGroup = new OptionGroup();
99         genOnlyGroup.setRequired(false);
100         genOnlyGroup.addOption(genEnumsOnly);
101         genOnlyGroup.addOption(genParserOnly);
102         options.addOptionGroup(genOnlyGroup);
103 
104         CommandLineParser CommandParser = new GnuParser();
105         CommandLine cmd;
106 
107         try {
108             cmd = CommandParser.parse(options, args);
109         } catch (ParseException e) {
110             System.err.println(e.getMessage());
111             help(options);
112             return;
113         }
114 
115         String[] xsdFile = cmd.getArgs();
116         String packageName = cmd.getOptionValue('p', null);
117         String outDir = cmd.getOptionValue('o', null);
118         boolean writer = cmd.hasOption('w');
119         boolean nullability = cmd.hasOption('n');
120         boolean genHas = cmd.hasOption('g');
121         boolean enumsOnly = cmd.hasOption('e');
122         boolean parserOnly = cmd.hasOption('x');
123         boolean booleanGetter = cmd.hasOption('b');
124 
125         if (xsdFile.length != 1 || packageName == null) {
126             System.err.println("Error: no xsd files or package name");
127             help(options);
128         }
129 
130         if (outDir == null) {
131             outDir = ".";
132         }
133 
134         XmlSchema xmlSchema = parse(xsdFile[0]);
135 
136         if (cmd.hasOption('j')) {
137             File packageDir = new File(Paths.get(outDir, packageName.replace(".", "/")).toString());
138             packageDir.mkdirs();
139             FileSystem fs = new FileSystem(packageDir);
140             JavaCodeGenerator javaCodeGenerator =
141                     new JavaCodeGenerator(xmlSchema, packageName, writer, nullability, genHas,
142                                           booleanGetter);
143             javaCodeGenerator.print(fs);
144         } else if (cmd.hasOption('c')) {
145             File includeDir = new File(Paths.get(outDir, "include").toString());
146             includeDir.mkdirs();
147             FileSystem fs = new FileSystem(new File(outDir));
148             int generators = enumsOnly ? CppCodeGenerator.GENERATE_ENUMS :
149                     (parserOnly ? CppCodeGenerator.GENERATE_PARSER :
150                             CppCodeGenerator.GENERATE_ENUMS | CppCodeGenerator.GENERATE_PARSER);
151             CppCodeGenerator cppCodeGenerator =
152                     new CppCodeGenerator(xmlSchema, packageName, writer, generators, booleanGetter);
153             cppCodeGenerator.print(fs);
154         }
155     }
156 
parse(String xsdFile)157     private static XmlSchema parse(String xsdFile) throws Exception {
158         XmlSchema xmlSchema;
159         try (FileInputStream in = new FileInputStream(xsdFile)) {
160             SAXParserFactory factory = SAXParserFactory.newInstance();
161             factory.setNamespaceAware(true);
162             SAXParser parser = factory.newSAXParser();
163             XsdHandler xsdHandler = new XsdHandler();
164             parser.parse(in, xsdHandler);
165             xmlSchema = xsdHandler.getSchema();
166         }
167         for (String file : xmlSchema.getIncludeList()) {
168             XmlSchema temp = parse(Paths.get(xsdFile).resolveSibling(file).toString());
169             xmlSchema.include(temp);
170         }
171         return xmlSchema;
172     }
173 
help(Options options)174     private static void help(Options options) {
175         new HelpFormatter().printHelp(
176                 "xsdc path/to/xsd_file.xsd","", options, null, true);
177         System.exit(1);
178     }
179 }
180