1
2
3
4
5
6
7
8
9
10
11
12 package org.hyphenType.dynamicproxy;
13
14 import java.io.ByteArrayOutputStream;
15 import java.io.PrintStream;
16 import java.io.PrintWriter;
17 import java.lang.annotation.Annotation;
18 import java.lang.reflect.Array;
19 import java.lang.reflect.Field;
20 import java.lang.reflect.InvocationHandler;
21 import java.lang.reflect.Method;
22 import java.text.MessageFormat;
23
24 import org.hyphenType.datastructure.Options;
25 import org.hyphenType.documentation.DocumentationFormatterEngine;
26 import org.hyphenType.exit.ExitStatusConstant;
27 import org.hyphenType.exit.ExitStatusHelper;
28 import org.hyphenType.exit.StatusCode;
29 import org.hyphenType.lexerparser.OptionValues;
30 import org.hyphenType.unittesting.NonExceptionalExit;
31
32
33
34
35
36 public abstract class AbstractArgumentsInvocationHandler<T extends Options<?>> implements InvocationHandler {
37
38
39
40
41 private final OptionValues<T> optionValues;
42
43
44
45
46 private final Class<? extends Options<?>> optionsInterface;
47
48
49
50
51 private final Class<? extends StatusCode> exitCodeEnumClass;
52
53
54
55
56 private final String[] rawArguments;
57
58 @SuppressWarnings("unchecked")
59 private DocumentationFormatterEngine defaultFormatter = null;
60
61
62
63
64
65
66
67
68
69 public AbstractArgumentsInvocationHandler(final OptionValues<T> optionValues, final Class<? extends Options<?>> optionsInterface, final Class<? extends StatusCode> exitCodeEnumClass, final String[] rawArguments) {
70 this.optionValues = optionValues;
71 this.optionsInterface = optionsInterface;
72 this.exitCodeEnumClass = exitCodeEnumClass;
73 this.rawArguments = rawArguments;
74 }
75
76
77
78
79
80
81 @SuppressWarnings("unchecked")
82 private DocumentationFormatterEngine defaultFormatter() {
83 if(defaultFormatter == null) {
84 defaultFormatter = DocumentationFormatterEngine.preferredFormatter(optionsInterface);
85 }
86 return defaultFormatter;
87 }
88
89 @SuppressWarnings("unchecked")
90 @Override
91 public final Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
92
93 if (method.equals(Object.class.getMethod("equals", Object.class))) {
94 return this.equals(args[0]);
95 }
96
97 if (method.equals(Object.class.getMethod("hashCode"))) {
98 return this.hashCode();
99 }
100
101 if (method.equals(Object.class.getMethod("toString"))) {
102 return this.toString();
103 }
104
105
106
107
108
109 if (method.equals(Options.class.getMethod("printDocumentation"))) {
110 printDocumentation();
111 return null;
112 }
113
114 if (method.equals(Options.class.getMethod("printDocumentation", PrintStream.class))) {
115 printDocumentation((PrintStream) args[0]);
116 return null;
117 }
118
119 if (method.equals(Options.class.getMethod("printDocumentation", Class.class))) {
120 printDocumentation((Class) args[0]);
121 return null;
122 }
123
124 if (method.equals(Options.class.getMethod("printDocumentation", Class.class, PrintStream.class))) {
125 printDocumentation((Class) args[0], (PrintStream) args[1]);
126 return null;
127 }
128
129 if (method.equals(Options.class.getMethod("unparsedArguments"))) {
130 return optionValues.unusedArguments();
131 }
132
133 if (method.equals(Options.class.getMethod("exit", Enum.class, Object[].class))) {
134 Object obj = args[0];
135 if (StatusCode.class.isAssignableFrom(obj.getClass())) {
136 StatusCode statusCode = (StatusCode) obj;
137 Object[] arguments = (Object[])args[1];
138 statusCode.beforeExit(new ExitStatusHelper(optionsInterface, (Enum<? extends StatusCode>) statusCode, null, arguments));
139 }
140 exit((Enum<?>) obj);
141 return null;
142 }
143
144 if (method.equals(Options.class.getMethod("exit", int.class, Object[].class))) {
145 int code = (Integer) args[0];
146
147 Enum<?>[] consts = (Enum[]) exitCodeEnumClass.getMethod("values").invoke(null);
148 if (code > -1 && code < consts.length) {
149 StatusCode statusCode = (StatusCode) consts[code];
150 Object[] arguments = (Object[])args[1];
151 statusCode.beforeExit(new ExitStatusHelper(optionsInterface, (Enum<? extends StatusCode>) statusCode, null, arguments));
152 }
153 exit(code);
154 }
155
156 if (method.equals(Options.class.getMethod("exit", Throwable.class))) {
157
158 Throwable t = (Throwable) args[0];
159 Class tClass = t.getClass();
160 if(tClass.equals(NonExceptionalExit.class)) {
161
162
163
164
165
166
167
168 return false;
169 }
170 do {
171 for(Field f : exitCodeEnumClass.getFields()) {
172 if(f.isAnnotationPresent(ExitStatusConstant.class)) {
173 ExitStatusConstant annotation = f.getAnnotation(ExitStatusConstant.class);
174 for(Class<? extends Throwable> ec: annotation.catches()) {
175 if(ec.equals(tClass)) {
176
177
178 String localizedMessage = t.getLocalizedMessage();
179 if(localizedMessage == null) {
180 localizedMessage = "";
181 }
182
183
184 ByteArrayOutputStream baos = new ByteArrayOutputStream();
185 PrintWriter pw = new PrintWriter(baos);
186 t.printStackTrace(pw);
187 pw.flush();
188 String stackTrace = new String(baos.toByteArray());
189
190 StatusCode s = StatusCode.class.cast(f.get(null));
191
192 s.beforeExit(new ExitStatusHelper(optionsInterface, (Enum<? extends StatusCode>) s, t, localizedMessage, stackTrace));
193 exit((Enum<? extends StatusCode>) s);
194 return true;
195 }
196 }
197 }
198 }
199 tClass = tClass.getSuperclass();
200 } while(!tClass.equals(Object.class));
201 return false;
202 }
203
204 if (method.equals(Options.class.getMethod("rawArguments"))) {
205 return rawArguments;
206 }
207
208 if (method.equals(Options.class.getMethod("localeMessage", String.class))) {
209 return defaultFormatter().getMessage((String)args[0], null);
210 }
211
212 if (method.equals(Options.class.getMethod("localeMessage", String.class, String.class))) {
213 return defaultFormatter().getMessage((String)args[0], (String)args[1]);
214 }
215
216 if (method.equals(Options.class.getMethod("formattedLocaleMessage", String.class, Object[].class))) {
217 String pattern = defaultFormatter().getMessage((String)args[0], null);
218 return MessageFormat.format(pattern, (Object[])args[1]);
219 }
220
221 if (method.equals(Options.class.getMethod("formattedLocaleMessageDefault", String.class, String.class, Object[].class))) {
222 String pattern = defaultFormatter().getMessage((String)args[0], (String)args[1]);
223 return MessageFormat.format(pattern, (Object[])args[2]);
224 }
225
226 if (method.getReturnType().isArray() && optionValues.getValue(method) == null) {
227 return Array.newInstance(method.getReturnType().getComponentType(), 0);
228 }
229
230 return optionValues.getValue(method);
231 }
232
233
234
235
236 protected final void printDocumentation() {
237 defaultFormatter().printDocumentation();
238 }
239
240
241
242
243
244
245 protected final void printDocumentation(final PrintStream printStream) {
246 defaultFormatter().printDocumentation(printStream);
247 }
248
249
250
251
252 protected final void printDocumentation(Class<? extends Annotation> formatterAnnotationClass) {
253 DocumentationFormatterEngine.buildFormatter(optionsInterface, formatterAnnotationClass).printDocumentation();
254 }
255
256
257
258
259
260
261 protected final void printDocumentation(Class<? extends Annotation> formatterAnnotationClass, final PrintStream printStream) {
262 DocumentationFormatterEngine.buildFormatter(optionsInterface, formatterAnnotationClass).printDocumentation(printStream);
263 }
264
265
266
267
268
269
270 protected abstract void exit(final Enum<?> e);
271
272
273
274
275
276
277 protected abstract void exit(final int code);
278
279 @Override
280 public final boolean equals(final Object obj) {
281 if (!(obj instanceof AbstractArgumentsInvocationHandler<?>)) {
282 return false;
283 }
284 AbstractArgumentsInvocationHandler<?> other = (AbstractArgumentsInvocationHandler<?>) obj;
285 return optionValues.equals(other.optionValues) && exitCodeEnumClass.equals(other.exitCodeEnumClass);
286 }
287
288 @Override
289 public final int hashCode() {
290 return optionValues.hashCode();
291 }
292
293 @Override
294 public final String toString() {
295 return optionValues.toString();
296 }
297 }