1
2
3
4
5
6
7
8
9
10
11
12 package org.hyphenType.documentation.lib;
13
14 import java.io.ByteArrayOutputStream;
15 import java.io.PrintStream;
16 import java.lang.annotation.ElementType;
17 import java.lang.annotation.Retention;
18 import java.lang.annotation.RetentionPolicy;
19 import java.lang.annotation.Target;
20 import java.util.StringTokenizer;
21
22 import org.hyphenType.datastructure.Options;
23 import org.hyphenType.datastructure.parser.option.StructureOption;
24 import org.hyphenType.datastructure.parser.option.StructureOptionArgument;
25 import org.hyphenType.datastructure.parser.simple.StructureSimpleArgument;
26 import org.hyphenType.documentation.Description;
27 import org.hyphenType.documentation.DocumentationFormatterEngine;
28 import org.hyphenType.exit.StatusCode;
29 import org.hyphenType.lexerparser.LexerParser;
30 import org.hyphenType.util.soc.StringParsingError;
31
32
33
34
35
36
37
38
39
40 public class StandardFormatterEngine<T extends Options<?>> extends DocumentationFormatterEngine<T, StandardFormatterEngine.StandardFormatter> {
41
42
43
44
45
46
47 @Retention(RetentionPolicy.RUNTIME)
48 @Target(ElementType.TYPE)
49 public @interface StandardFormatter {
50
51
52
53
54 final int DEFAULT_DESCRIPTION_INDENT = 20;
55
56
57
58
59 final int DEFAULT_MAX_COLUMN = 80;
60
61
62
63
64 @Description("The label that represents the executing program.")
65 String program() default "program";
66
67
68
69
70 @Description("The \"usage\" label.")
71 String usage() default "usage";
72
73
74
75
76 @Description("The \"options\" label.")
77 String options() default "options";
78
79
80
81
82 @Description("The \"Status code\" label.")
83 String statusCode() default "Status code";
84
85
86
87
88 @Description("Whether lines should only break at spaces or if lines can break in the middle of a word.")
89 boolean lineBreakAtSpace() default true;
90
91
92
93
94 @Description("The number of characters before the description text.")
95 int descriptionIndent() default DEFAULT_DESCRIPTION_INDENT;
96
97
98
99
100 @Description("The maximum number of columns before a line break.\nThis value should be larger than descriptionIndent.")
101 int maxColumn() default DEFAULT_MAX_COLUMN;
102 }
103
104 @SuppressWarnings("unchecked")
105 @Override
106 public final void printDocumentation(final PrintStream pw, final LexerParser<T> lexPar, final StandardFormatter annotation) {
107
108 try {
109
110 pw.format("%s: %s ", annotation.usage(), annotation.program());
111 if (lexPar.getParsedOptions().size() != 0) {
112 pw.format("[%s] ", annotation.options());
113 }
114
115 for (StructureSimpleArgument simpleArgument : lexPar.getSimpleArguments()) {
116 if (!simpleArgument.isMandatory()) {
117 pw.print("[");
118 }
119 pw.print(getOptionsInterfaceValue(simpleArgument.getName(), simpleArgument.getName()));
120 if (simpleArgument.method.getReturnType().isArray()) {
121 pw.print("...");
122 }
123 if (!simpleArgument.isMandatory()) {
124 pw.print("]");
125 }
126 pw.print(" ");
127 }
128
129 pw.println();
130 pw.println(print("", getOptionsInterfaceValue("", lexPar.getArgsObject().description()), 0, 0, annotation.maxColumn(), annotation.lineBreakAtSpace()));
131
132 String simpleArgumentDescriptions = "";
133 for (StructureSimpleArgument simpleArgument : lexPar.getSimpleArguments()) {
134 simpleArgumentDescriptions += print(simpleArgument.getName(), simpleArgument.getDescription(), 2, annotation.descriptionIndent(), annotation.maxColumn(), annotation.lineBreakAtSpace()) + "\n";
135 }
136 if (simpleArgumentDescriptions.length() > 0) {
137 pw.println();
138 pw.println("Arguments");
139 pw.println();
140 pw.print(simpleArgumentDescriptions);
141 }
142
143 pw.println();
144 pw.println("Options");
145 pw.println();
146
147 for (StructureOption parsedOption : lexPar.getParsedOptions()) {
148 String optionName = "";
149 for (String alternative : parsedOption.alternatives) {
150 if (optionName.contains(lexPar.getArgsObject().singleHyphen())) {
151 optionName += ", ";
152 }
153 if (lexPar.getArgsObject().doubleHyphenInLongOptions() && alternative.length() > 1) {
154 optionName += lexPar.getArgsObject().doubleHyphen();
155 } else {
156 optionName += lexPar.getArgsObject().singleHyphen();
157 }
158 optionName += alternative;
159 if (parsedOption.value != null) {
160 if (!parsedOption.value.mandatory) {
161 optionName += "[";
162 }
163 optionName += lexPar.getArgsObject().equals() + parsedOption.value.name;
164 if (!parsedOption.value.mandatory) {
165 optionName += "]";
166 }
167 }
168 }
169
170 for (StructureOptionArgument argument : parsedOption.arguments) {
171 optionName += " ";
172 if (!argument.isMandatory()) {
173 optionName += "[";
174 } else {
175 optionName += "<";
176 }
177 optionName += argument.getName();
178 if (argument.method.getReturnType().isArray()) {
179 optionName += "...";
180 }
181 if (!argument.isMandatory()) {
182 optionName += "]";
183 } else {
184 optionName += ">";
185 }
186 }
187
188 String optionDocumentation = getOptionsInterfaceValue(parsedOption.method.getName(), parsedOption.description);
189 pw.println(print(optionName, optionDocumentation, 2, annotation.descriptionIndent(), annotation.maxColumn(), annotation.lineBreakAtSpace()));
190 }
191
192 if (lexPar.getArgsObject().documentStatusCodes()) {
193
194 pw.println();
195 pw.println(annotation.statusCode());
196
197 for (StatusCode statusCode : lexPar.getArgsObject().statusCodeEnum().getEnumConstants()) {
198 Enum<?> enumConstant = Enum.valueOf((Class<? extends Enum>) lexPar.getArgsObject().statusCodeEnum(), statusCode.toString());
199 pw.println(print(Integer.toString(enumConstant.ordinal()), getStatusCodeUserDescription(enumConstant), 2, annotation.descriptionIndent(), annotation.maxColumn(), annotation.lineBreakAtSpace()));
200 }
201 }
202 } catch (StringParsingError e) {
203 e.printStackTrace();
204 }
205 }
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227 public final String print(final String title, final String description, final int titleIndent, final int descriptionIndent, final int maxColumn, final boolean lineBreakAtSpace) {
228
229 ByteArrayOutputStream baos = new ByteArrayOutputStream();
230 PrintStream pw = new PrintStream(baos);
231
232 String line = space(titleIndent) + title;
233 if (2 + title.length() > descriptionIndent - 1) {
234 pw.println(line);
235 line = space(descriptionIndent);
236 } else {
237 line += space(descriptionIndent - line.length());
238 }
239
240 if (lineBreakAtSpace) {
241 StringTokenizer st = new StringTokenizer(description.trim());
242 while (st.hasMoreTokens()) {
243 String word = st.nextToken();
244 if (line.length() + word.length() > maxColumn) {
245
246 if (word.length() > maxColumn - descriptionIndent) {
247 while (word.length() > maxColumn - descriptionIndent) {
248
249
250 int removedRange = maxColumn - line.length();
251 line += word.substring(0, removedRange);
252 pw.println(line);
253 word = word.substring(removedRange, word.length());
254 line = space(descriptionIndent);
255 }
256 if (word.length() > 0) {
257 line += word;
258 }
259 if (st.hasMoreTokens() && line.length() + 1 <= maxColumn) {
260 line += " ";
261 }
262 } else {
263 pw.println(line);
264 line = space(descriptionIndent) + word;
265 if (st.hasMoreTokens() && line.length() + 1 <= maxColumn) {
266 line += " ";
267 }
268 }
269 } else {
270 line += word;
271 if (st.hasMoreTokens() && line.length() + 1 <= maxColumn) {
272 line += " ";
273 }
274 }
275 }
276 } else {
277 for (char b : description.toCharArray()) {
278 if (line.equals("")) {
279 line = space(descriptionIndent);
280 pw.println();
281 }
282 line += b;
283 if (line.length() == maxColumn) {
284 pw.print(line);
285 line = "";
286 }
287 }
288 }
289 if (line.length() > descriptionIndent) {
290 pw.print(line);
291 }
292 return new String(baos.toByteArray());
293 }
294
295
296
297
298
299
300
301 private String space(final int n) {
302 String s = "";
303 for (int i = 0; i < n; i++) {
304 s += " ";
305 }
306 return s;
307 }
308 }