1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package cz.hobrasoft.pdfmu;
18
19 import static cz.hobrasoft.pdfmu.error.ErrorType.INPUT_NOT_FOUND;
20 import static cz.hobrasoft.pdfmu.error.ErrorType.PARSER_EXPECTED_ONE_ARGUMENT;
21 import static cz.hobrasoft.pdfmu.error.ErrorType.PARSER_INVALID_CHOICE;
22 import static cz.hobrasoft.pdfmu.error.ErrorType.PARSER_TOO_FEW_ARGUMENTS;
23 import static cz.hobrasoft.pdfmu.error.ErrorType.PARSER_UNKNOWN;
24 import static cz.hobrasoft.pdfmu.error.ErrorType.PARSER_UNRECOGNIZED_ARGUMENT;
25 import static cz.hobrasoft.pdfmu.error.ErrorType.PARSER_UNRECOGNIZED_COMMAND;
26 import cz.hobrasoft.pdfmu.operation.Operation;
27 import cz.hobrasoft.pdfmu.operation.OperationAttach;
28 import cz.hobrasoft.pdfmu.operation.OperationException;
29 import cz.hobrasoft.pdfmu.operation.OperationInspect;
30 import cz.hobrasoft.pdfmu.operation.metadata.OperationMetadataSet;
31 import cz.hobrasoft.pdfmu.operation.signature.OperationSignatureAdd;
32 import cz.hobrasoft.pdfmu.operation.version.OperationVersionSet;
33 import java.io.IOException;
34 import java.io.InputStream;
35 import static java.nio.charset.StandardCharsets.US_ASCII;
36 import java.util.ArrayList;
37 import java.util.Arrays;
38 import java.util.HashSet;
39 import java.util.LinkedHashMap;
40 import java.util.Map;
41 import java.util.Properties;
42 import java.util.Set;
43 import java.util.logging.LogManager;
44 import java.util.logging.Logger;
45 import net.sourceforge.argparse4j.ArgumentParsers;
46 import net.sourceforge.argparse4j.impl.Arguments;
47 import net.sourceforge.argparse4j.inf.ArgumentParser;
48 import net.sourceforge.argparse4j.inf.ArgumentParserException;
49 import net.sourceforge.argparse4j.inf.Namespace;
50 import net.sourceforge.argparse4j.inf.Subparsers;
51 import net.sourceforge.argparse4j.internal.HelpScreenException;
52 import net.sourceforge.argparse4j.internal.UnrecognizedArgumentException;
53 import net.sourceforge.argparse4j.internal.UnrecognizedCommandException;
54 import org.apache.commons.io.IOUtils;
55
56
57
58
59
60
61 public class Main {
62
63 private static void disableLoggers() {
64
65 LogManager.getLogManager().reset();
66 }
67
68 private static final Logger logger = Logger.getLogger(Main.class.getName());
69
70 static {
71
72
73
74
75
76 System.setProperty("java.util.logging.SimpleFormatter.format", "%4$s: %5$s%n");
77 }
78
79 private static ArgumentParser createBasicParser() {
80
81 ArgumentParser parser = ArgumentParsers.newArgumentParser("pdfmu")
82 .description("PDF Manipulation Utility")
83 .defaultHelp(true);
84
85 parser.version(getProjectVersion());
86 parser.addArgument("-v", "--version")
87 .help("show version and exit")
88 .action(Arguments.version());
89
90 parser.addArgument("--legal-notice")
91 .help("show legal notice and exit")
92 .action(new PrintAndExitAction(getLegalNotice()));
93
94
95 parser.addArgument("--output-format")
96 .choices("text", "json")
97 .setDefault("text")
98 .type(String.class)
99 .help("format of stderr output");
100
101 return parser;
102 }
103
104 private static final String POM_PROPERTIES_RESOURCE_NAME = "pom.properties";
105 private static final Properties POM_PROPERTIES = new Properties();
106
107 private static void loadPomProperties() {
108 ClassLoader classLoader = Main.class.getClassLoader();
109 InputStream in = classLoader.getResourceAsStream(POM_PROPERTIES_RESOURCE_NAME);
110 if (in != null) {
111 try {
112 POM_PROPERTIES.load(in);
113 } catch (IOException ex) {
114 logger.severe(String.format("Could not load the POM properties file: %s", ex));
115 }
116 try {
117 in.close();
118 } catch (IOException ex) {
119 logger.severe(String.format("Could not close the POM properties file: %s", ex));
120 }
121 } else {
122 logger.severe("Could not open the POM properties file.");
123 }
124 }
125
126 private static final String LEGAL_NOTICE_RESOURCE_NAME = "cz/hobrasoft/pdfmu/legalNotice.txt";
127 private static String legalNotice;
128
129 private static void loadLegalNotice() {
130 ClassLoader classLoader = Main.class.getClassLoader();
131 InputStream in = classLoader.getResourceAsStream(LEGAL_NOTICE_RESOURCE_NAME);
132 if (in != null) {
133 try {
134 legalNotice = IOUtils.toString(in, US_ASCII);
135 } catch (IOException ex) {
136 logger.severe(String.format("Could not load the legal notice file: %s", ex));
137 }
138 try {
139 in.close();
140 } catch (IOException ex) {
141 logger.severe(String.format("Could not close the legal notice file: %s", ex));
142 }
143 } else {
144 logger.severe("Could not open the legal notice file.");
145 }
146 }
147
148 static {
149 loadPomProperties();
150 loadLegalNotice();
151 assert legalNotice != null;
152 }
153
154 public static String getProjectVersion() {
155 return POM_PROPERTIES.getProperty("projectVersion");
156 }
157
158 public static String getProjectCopyright() {
159 return POM_PROPERTIES.getProperty("copyright");
160 }
161
162 public static String getLegalNotice() {
163 return String.format("%1$s\n\n%2$s", getProjectCopyright(), legalNotice);
164 }
165
166
167
168
169
170
171
172
173 private static ArgumentParser createFullParser(Map<String, Operation> operations) {
174
175 ArgumentParser parser = createBasicParser();
176
177
178 Subparsers subparsers = parser.addSubparsers()
179 .title("operations")
180 .metavar("OPERATION")
181 .help("operation to execute")
182 .dest("operation");
183
184
185 for (Map.Entry<String, Operation> e : operations.entrySet()) {
186 String name = e.getKey();
187 Operation operation = e.getValue();
188 operation.configureSubparser(subparsers.addParser(name));
189 }
190
191 return parser;
192 }
193
194 private static OperationException apeToOe(ArgumentParserException e) {
195 Set<ExceptionMessagePattern> patterns = new HashSet<>();
196
197 patterns.add(new ExceptionMessagePattern(INPUT_NOT_FOUND,
198 "argument (?<argument>.*): Insufficient permissions to read file: \'(?<file>.*)\'",
199 Arrays.asList(new String[]{"argument", "file"})));
200
201
202 patterns.add(new ExceptionMessagePattern(PARSER_UNRECOGNIZED_ARGUMENT,
203 "unrecognized arguments: '(?<argument>.*)'",
204 Arrays.asList(new String[]{"argument"})));
205
206
207 patterns.add(new ExceptionMessagePattern(PARSER_INVALID_CHOICE,
208 "argument (?<argument>.*): invalid choice: '(?<choice>.*)' \\(choose from \\{(?<validChoices>.*)\\}\\)",
209 Arrays.asList(new String[]{"argument", "choice", "validChoices"})));
210
211
212 patterns.add(new ExceptionMessagePattern(PARSER_UNRECOGNIZED_COMMAND,
213 "invalid choice: '(?<command>.*)' \\(choose from (?<validCommands>.*)\\)",
214 Arrays.asList(new String[]{"command", "validCommands"})));
215
216
217 patterns.add(new ExceptionMessagePattern(PARSER_TOO_FEW_ARGUMENTS,
218 "too few arguments",
219 new ArrayList<String>()));
220
221 patterns.add(new ExceptionMessagePattern(PARSER_EXPECTED_ONE_ARGUMENT,
222 "argument (?<argument>.*): expected one argument",
223 Arrays.asList(new String[]{"argument"})));
224
225 OperationException oe = null;
226 for (ExceptionMessagePattern p : patterns) {
227 oe = p.getOperationException(e);
228 if (oe != null) {
229 break;
230 }
231 }
232
233 if (oe == null) {
234
235 oe = new OperationException(PARSER_UNKNOWN, e);
236 }
237
238 assert oe != null;
239 return oe;
240 }
241
242
243
244
245
246
247 public static void main(String[] args) {
248 int exitStatus = 0;
249
250
251 Map<String, Operation> operations = new LinkedHashMap<>();
252 operations.put("inspect", OperationInspect.getInstance());
253 operations.put("update-version", OperationVersionSet.getInstance());
254 operations.put("update-properties", OperationMetadataSet.getInstance());
255 operations.put("attach", OperationAttach.getInstance());
256 operations.put("sign", OperationSignatureAdd.getInstance());
257
258
259 ArgumentParser parser = createFullParser(operations);
260
261
262 Namespace namespace = null;
263 try {
264
265
266
267
268
269 namespace = parser.parseArgs(args);
270 } catch (HelpScreenException e) {
271 parser.handleError(e);
272 } catch (UnrecognizedCommandException e) {
273 exitStatus = PARSER_UNRECOGNIZED_COMMAND.getCode();
274 parser.handleError(e);
275 } catch (UnrecognizedArgumentException e) {
276 exitStatus = PARSER_UNRECOGNIZED_ARGUMENT.getCode();
277 parser.handleError(e);
278 } catch (ArgumentParserException ape) {
279 OperationException oe = apeToOe(ape);
280 exitStatus = oe.getCode();
281
282
283
284
285 parser.handleError(ape);
286 }
287
288 if (namespace == null) {
289 System.exit(exitStatus);
290 }
291
292 assert exitStatus == 0;
293
294
295 WritingMapper wm = null;
296
297
298 String operationName = namespace.getString("operation");
299 assert operationName != null;
300
301
302 assert operations.containsKey(operationName);
303 Operation operation = operations.get(operationName);
304 assert operation != null;
305
306
307 String outputFormat = namespace.getString("output_format");
308 switch (outputFormat) {
309 case "json":
310
311 disableLoggers();
312
313 wm = new WritingMapper();
314 operation.setWritingMapper(wm);
315 break;
316 case "text":
317
318 TextOutput to = new TextOutput(System.err);
319 operation.setTextOutput(to);
320 break;
321 default:
322 assert false;
323 }
324
325
326 try {
327 operation.execute(namespace);
328 } catch (OperationException ex) {
329 exitStatus = ex.getCode();
330
331
332 logger.severe(ex.getLocalizedMessage());
333 Throwable cause = ex.getCause();
334 if (cause != null && cause.getMessage() != null) {
335 logger.severe(cause.getLocalizedMessage());
336 }
337
338 if (wm != null) {
339
340 ex.writeInWritingMapper(wm);
341 }
342 }
343 System.exit(exitStatus);
344 }
345 }