001package ball.util.ant.taskdefs;
002/*-
003 * ##########################################################################
004 * Utilities
005 * $Id: TypeTask.IsAssignableFrom.html 5431 2020-02-12 19:03:17Z ball $
006 * $HeadURL: svn+ssh://svn.hcf.dev/var/spool/scm/repository.svn/hcf-dev/blog/2019-07-08-fivethirtyeight-best-scrabble-string/src/main/resources/javadoc/src-html/ball/util/ant/taskdefs/TypeTask.IsAssignableFrom.html $
007 * %%
008 * Copyright (C) 2008 - 2020 Allen D. Ball
009 * %%
010 * Licensed under the Apache License, Version 2.0 (the "License");
011 * you may not use this file except in compliance with the License.
012 * You may obtain a copy of the License at
013 *
014 *      http://www.apache.org/licenses/LICENSE-2.0
015 *
016 * Unless required by applicable law or agreed to in writing, software
017 * distributed under the License is distributed on an "AS IS" BASIS,
018 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
019 * See the License for the specific language governing permissions and
020 * limitations under the License.
021 * ##########################################################################
022 */
023import ball.beans.PropertyDescriptorsTableModel;
024import ball.swing.table.SimpleTableModel;
025import java.beans.BeanDescriptor;
026import java.beans.BeanInfo;
027import java.beans.Introspector;
028import java.beans.PropertyDescriptor;
029import java.lang.reflect.Constructor;
030import java.lang.reflect.Field;
031import java.lang.reflect.Method;
032import java.lang.reflect.TypeVariable;
033import java.net.URL;
034import java.util.ArrayList;
035import lombok.Getter;
036import lombok.NoArgsConstructor;
037import lombok.Setter;
038import lombok.ToString;
039import lombok.experimental.Accessors;
040import org.apache.commons.lang3.ClassUtils;
041import org.apache.tools.ant.BuildException;
042import org.apache.tools.ant.Task;
043import org.apache.tools.ant.util.ClasspathUtils;
044
045import static ball.lang.Punctuation.GT;
046import static ball.lang.Punctuation.LT;
047import static lombok.AccessLevel.PROTECTED;
048import static org.apache.commons.lang3.StringUtils.EMPTY;
049
050/**
051 * Abstract {@link.uri http://ant.apache.org/ Ant} {@link Task} to specify a
052 * type ({@link Class}).
053 *
054 * {@ant.task}
055 *
056 * @author {@link.uri mailto:ball@hcf.dev Allen D. Ball}
057 * @version $Revision: 5431 $
058 */
059@NoArgsConstructor(access = PROTECTED)
060public abstract class TypeTask extends Task
061                               implements AnnotatedAntTask,
062                                          ClasspathDelegateAntTask {
063    @Getter @Setter @Accessors(chain = true, fluent = true)
064    private ClasspathUtils.Delegate delegate = null;
065    @NotNull @Getter
066    private String type = null;
067
068    public void setType(String string) {
069        type = string;
070        ClasspathDelegateAntTask.super.setClassname(type);
071    }
072
073    @Override
074    public void setClassname(String string) { setType(string); }
075
076    @Override
077    public void init() throws BuildException {
078        super.init();
079        ClasspathDelegateAntTask.super.init();
080    }
081
082    @Override
083    public void execute() throws BuildException {
084        super.execute();
085        AnnotatedAntTask.super.execute();
086    }
087
088    /**
089     * {@link.uri http://ant.apache.org/ Ant}
090     * {@link org.apache.tools.ant.Task} to display {@link BeanInfo}
091     * for a specified {@link Class}.
092     *
093     * {@ant.task}
094     */
095    @AntTask("bean-info-for")
096    @NoArgsConstructor @ToString
097    public static class BeanInfoFor extends TypeTask {
098        @Override
099        public void execute() throws BuildException {
100            super.execute();
101
102            try {
103                log(Introspector.getBeanInfo(getClassForName(getType())));
104            } catch (BuildException exception) {
105                throw exception;
106            } catch (Throwable throwable) {
107                throwable.printStackTrace();
108                throw new BuildException(throwable);
109            }
110        }
111
112        private void log(BeanInfo bean) {
113            log(new BeanHeaderTableModel(bean.getBeanDescriptor()));
114            log();
115            log(new TableModelImpl(bean.getPropertyDescriptors()));
116            log(bean.getAdditionalBeanInfo());
117        }
118
119        private void log(BeanInfo[] beans) {
120            if (beans != null) {
121                for (BeanInfo bean : beans) {
122                    log();
123                    log(bean);
124                }
125            }
126        }
127
128        private class BeanHeaderTableModel extends SimpleTableModel {
129            private static final long serialVersionUID = 2338583773511453873L;
130
131            public BeanHeaderTableModel(BeanDescriptor descriptor) {
132                super(new Object[][] { }, 2);
133
134                row("Bean Class:",
135                    descriptor.getBeanClass().getName());
136
137                if (descriptor.getCustomizerClass() != null) {
138                    row("Customizer Class:",
139                        descriptor.getCustomizerClass().getName());
140                }
141            }
142        }
143
144        private class TableModelImpl extends PropertyDescriptorsTableModel {
145            private static final long serialVersionUID = -5817972183666452609L;
146
147            public TableModelImpl(PropertyDescriptor[] rows) { super(rows); }
148
149            @Override
150            public Object getValueAt(int y, int x) {
151                Object value = super.getValueAt(y, x);
152
153                if (value instanceof Class<?>) {
154                    value = ((Class<?>) value).getCanonicalName();
155                }
156
157                return value;
158            }
159        }
160    }
161
162    /**
163     * {@link.uri http://ant.apache.org/ Ant}
164     * {@link org.apache.tools.ant.Task} to display superclasses of a
165     * specified {@link Class}.
166     *
167     * {@ant.task}
168     */
169    @AntTask("is-assignable-from")
170    @NoArgsConstructor @ToString
171    public static class IsAssignableFrom extends TypeTask {
172        @NotNull @Getter @Setter
173        private String subtype = null;
174
175        @Override
176        public void execute() throws BuildException {
177            super.execute();
178
179            try {
180                Class<?> supertype = getClassForName(getType());
181                Class<?> subtype = getClassForName(getSubtype());
182
183                log(supertype.getName() + " is "
184                    + (supertype.isAssignableFrom(subtype) ? EMPTY : "not ")
185                    + "assignable from " + subtype.getName());
186            } catch (BuildException exception) {
187                throw exception;
188            } catch (Throwable throwable) {
189                throwable.printStackTrace();
190                throw new BuildException(throwable);
191            }
192        }
193    }
194
195    /**
196     * {@link.uri http://ant.apache.org/ Ant}
197     * {@link org.apache.tools.ant.Task} to display members of a specified
198     * {@link Class}.
199     *
200     * {@ant.task}
201     */
202    @AntTask("members-of")
203    @NoArgsConstructor @ToString
204    public static class MembersOf extends TypeTask {
205        @Override
206        public void execute() throws BuildException {
207            super.execute();
208
209            try {
210                Class<?> type = getClassForName(getType());
211
212                log(String.valueOf(type));
213
214                for (Constructor<?> constructor :
215                         type.getDeclaredConstructors()) {
216                    log(constructor.toGenericString());
217                }
218
219                for (Field field : type.getDeclaredFields()) {
220                    log(field.toGenericString());
221                }
222
223                for (Method method : type.getDeclaredMethods()) {
224                    log(method.toGenericString());
225                }
226
227                for (Class<?> cls : type.getDeclaredClasses()) {
228                    log(cls.toString());
229                }
230            } catch (BuildException exception) {
231                throw exception;
232            } catch (Throwable throwable) {
233                throwable.printStackTrace();
234                throw new BuildException(throwable);
235            }
236        }
237    }
238
239    /**
240     * {@link.uri http://ant.apache.org/ Ant}
241     * {@link org.apache.tools.ant.Task} to display resource path to a
242     * specified {@link Class}.
243     *
244     * {@ant.task}
245     */
246    @AntTask("resource-path-to")
247    @NoArgsConstructor @ToString
248    public static class ResourcePathTo extends TypeTask {
249        @Override
250        public void execute() throws BuildException {
251            super.execute();
252
253            try {
254                Class<?> type = getClassForName(getType());
255
256                log(String.valueOf(type));
257
258                URL url =
259                    type.getClass()
260                    .getResource(type.getSimpleName() + ".class");
261
262                log(String.valueOf(url));
263            } catch (BuildException exception) {
264                throw exception;
265            } catch (Throwable throwable) {
266                throwable.printStackTrace();
267                throw new BuildException(throwable);
268            }
269        }
270    }
271
272    /**
273     * {@link.uri http://ant.apache.org/ Ant}
274     * {@link org.apache.tools.ant.Task} to display superclasses of a
275     * specified {@link Class}.
276     *
277     * {@ant.task}
278     */
279    @AntTask("superclasses-of")
280    @NoArgsConstructor @ToString
281    public static class SuperclassesOf extends TypeTask {
282        @Override
283        public void execute() throws BuildException {
284            super.execute();
285
286            try {
287                ArrayList<Class<?>> list = new ArrayList<>();
288                Class<?> type = getClassForName(getType());
289
290                list.add(type);
291                list.addAll(ClassUtils.getAllSuperclasses(type));
292                list.addAll(ClassUtils.getAllInterfaces(type));
293
294                for (Class<?> superclass : list) {
295                    log(toString(superclass));
296                }
297            } catch (BuildException exception) {
298                throw exception;
299            } catch (Throwable throwable) {
300                throwable.printStackTrace();
301                throw new BuildException(throwable);
302            }
303        }
304
305        private String toString(Class<?> type) {
306            StringBuilder buffer = new StringBuilder(type.getName());
307
308            if (type.getTypeParameters().length > 0) {
309                buffer.append(LT.lexeme());
310
311                for (TypeVariable<?> parameter : type.getTypeParameters()) {
312                    buffer.append(parameter);
313                }
314
315                buffer.append(GT.lexeme());
316            }
317
318            return buffer.toString();
319        }
320    }
321}