001package ball.util.ant.taskdefs;
002/*-
003 * ##########################################################################
004 * Utilities
005 * $Id: TypeTask.java 6118 2020-06-04 19:31:45Z ball $
006 * $HeadURL: svn+ssh://svn.hcf.dev/var/spool/scm/repository.svn/ball-util/trunk/src/main/java/ball/util/ant/taskdefs/TypeTask.java $
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.lang.reflect.JavaLangReflectMethods;
025import ball.swing.table.SimpleTableModel;
026import java.beans.BeanDescriptor;
027import java.beans.BeanInfo;
028import java.beans.Introspector;
029import java.beans.PropertyDescriptor;
030import java.net.URL;
031import java.util.ArrayList;
032import java.util.stream.Stream;
033import lombok.Getter;
034import lombok.NoArgsConstructor;
035import lombok.Setter;
036import lombok.ToString;
037import lombok.experimental.Accessors;
038import org.apache.commons.lang3.ClassUtils;
039import org.apache.tools.ant.BuildException;
040import org.apache.tools.ant.Task;
041import org.apache.tools.ant.util.ClasspathUtils;
042
043import static lombok.AccessLevel.PROTECTED;
044import static org.apache.commons.lang3.StringUtils.EMPTY;
045
046/**
047 * Abstract {@link.uri http://ant.apache.org/ Ant} {@link Task} to specify a
048 * type ({@link Class}).
049 *
050 * {@ant.task}
051 *
052 * @author {@link.uri mailto:ball@hcf.dev Allen D. Ball}
053 * @version $Revision: 6118 $
054 */
055@NoArgsConstructor(access = PROTECTED)
056public abstract class TypeTask extends Task
057                               implements AnnotatedAntTask,
058                                          ClasspathDelegateAntTask,
059                                          JavaLangReflectMethods {
060    @Getter @Setter @Accessors(chain = true, fluent = true)
061    private ClasspathUtils.Delegate delegate = null;
062    @NotNull @Getter
063    private String type = null;
064
065    public void setType(String string) {
066        type = string;
067        ClasspathDelegateAntTask.super.setClassname(type);
068    }
069
070    @Override
071    public void setClassname(String string) { setType(string); }
072
073    @Override
074    public void init() throws BuildException {
075        super.init();
076        ClasspathDelegateAntTask.super.init();
077    }
078
079    @Override
080    public void execute() throws BuildException {
081        super.execute();
082        AnnotatedAntTask.super.execute();
083    }
084
085    /**
086     * {@link.uri http://ant.apache.org/ Ant}
087     * {@link org.apache.tools.ant.Task} to display {@link BeanInfo}
088     * for a specified {@link Class}.
089     *
090     * {@ant.task}
091     */
092    @AntTask("bean-info-for")
093    @NoArgsConstructor @ToString
094    public static class BeanInfoFor extends TypeTask {
095        @Override
096        public void execute() throws BuildException {
097            super.execute();
098
099            try {
100                log(Introspector.getBeanInfo(getClassForName(getType())));
101            } catch (BuildException exception) {
102                throw exception;
103            } catch (Throwable throwable) {
104                throwable.printStackTrace();
105                throw new BuildException(throwable);
106            }
107        }
108
109        private void log(BeanInfo bean) {
110            log(new BeanHeaderTableModel(bean.getBeanDescriptor()));
111            log();
112            log(new TableModelImpl(bean.getPropertyDescriptors()));
113            log(bean.getAdditionalBeanInfo());
114        }
115
116        private void log(BeanInfo[] beans) {
117            if (beans != null) {
118                for (BeanInfo bean : beans) {
119                    log();
120                    log(bean);
121                }
122            }
123        }
124
125        @ToString
126        private class BeanHeaderTableModel extends SimpleTableModel {
127            private static final long serialVersionUID = -154550807608756372L;
128
129            public BeanHeaderTableModel(BeanDescriptor descriptor) {
130                super(new Object[][] { }, 2);
131
132                row("Bean Class:",
133                    descriptor.getBeanClass().getName());
134
135                if (descriptor.getCustomizerClass() != null) {
136                    row("Customizer Class:",
137                        descriptor.getCustomizerClass().getName());
138                }
139            }
140        }
141
142        @ToString
143        private class TableModelImpl extends PropertyDescriptorsTableModel {
144            private static final long serialVersionUID = 3641199494457667293L;
145
146            public TableModelImpl(PropertyDescriptor[] rows) { super(rows); }
147
148            @Override
149            public Object getValueAt(int y, int x) {
150                Object value = super.getValueAt(y, x);
151
152                if (value instanceof Class<?>) {
153                    value = ((Class<?>) value).getCanonicalName();
154                }
155
156                return value;
157            }
158        }
159    }
160
161    /**
162     * {@link.uri http://ant.apache.org/ Ant}
163     * {@link org.apache.tools.ant.Task} to display superclasses of a
164     * specified {@link Class}.
165     *
166     * {@ant.task}
167     */
168    @AntTask("is-assignable-from")
169    @NoArgsConstructor @ToString
170    public static class IsAssignableFrom extends TypeTask {
171        @NotNull @Getter @Setter
172        private String subtype = null;
173
174        @Override
175        public void execute() throws BuildException {
176            super.execute();
177
178            try {
179                Class<?> supertype = getClassForName(getType());
180                Class<?> subtype = getClassForName(getSubtype());
181
182                log(supertype.getName() + " is "
183                    + (supertype.isAssignableFrom(subtype) ? EMPTY : "not ")
184                    + "assignable from " + subtype.getName());
185            } catch (BuildException exception) {
186                throw exception;
187            } catch (Throwable throwable) {
188                throwable.printStackTrace();
189                throw new BuildException(throwable);
190            }
191        }
192    }
193
194    /**
195     * {@link.uri http://ant.apache.org/ Ant}
196     * {@link org.apache.tools.ant.Task} to display members of a specified
197     * {@link Class}.
198     *
199     * {@ant.task}
200     */
201    @AntTask("members-of")
202    @NoArgsConstructor @ToString
203    public static class MembersOf extends TypeTask {
204        @Override
205        public void execute() throws BuildException {
206            super.execute();
207
208            try {
209                Class<?> type = getClassForName(getType());
210
211                log(type(type));
212                Stream.of(type.getDeclaredClasses())
213                    .forEach(t -> log(type(t)));
214                Stream.of(type.getDeclaredFields())
215                    .forEach(t -> log(declaration(t)));
216                Stream.of(type.getDeclaredConstructors())
217                    .forEach(t -> log(declaration(t)));
218                Stream.of(type.getDeclaredMethods())
219                    .forEach(t -> log(declaration(t)));
220            } catch (BuildException exception) {
221                throw exception;
222            } catch (Throwable throwable) {
223                throwable.printStackTrace();
224                throw new BuildException(throwable);
225            }
226        }
227    }
228
229    /**
230     * {@link.uri http://ant.apache.org/ Ant}
231     * {@link org.apache.tools.ant.Task} to display resource path to a
232     * specified {@link Class}.
233     *
234     * {@ant.task}
235     */
236    @AntTask("resource-path-to")
237    @NoArgsConstructor @ToString
238    public static class ResourcePathTo extends TypeTask {
239        @Override
240        public void execute() throws BuildException {
241            super.execute();
242
243            try {
244                Class<?> type = getClassForName(getType());
245
246                log(String.valueOf(type));
247
248                URL url =
249                    type.getClass()
250                    .getResource(type.getSimpleName() + ".class");
251
252                log(String.valueOf(url));
253            } catch (BuildException exception) {
254                throw exception;
255            } catch (Throwable throwable) {
256                throwable.printStackTrace();
257                throw new BuildException(throwable);
258            }
259        }
260    }
261
262    /**
263     * {@link.uri http://ant.apache.org/ Ant}
264     * {@link org.apache.tools.ant.Task} to display superclasses of a
265     * specified {@link Class}.
266     *
267     * {@ant.task}
268     */
269    @AntTask("superclasses-of")
270    @NoArgsConstructor @ToString
271    public static class SuperclassesOf extends TypeTask {
272        @Override
273        public void execute() throws BuildException {
274            super.execute();
275
276            try {
277                ArrayList<Class<?>> list = new ArrayList<>();
278                Class<?> type = getClassForName(getType());
279
280                list.add(type);
281                list.addAll(ClassUtils.getAllSuperclasses(type));
282                list.addAll(ClassUtils.getAllInterfaces(type));
283
284                for (Class<?> superclass : list) {
285                    log(type(superclass));
286                }
287            } catch (BuildException exception) {
288                throw exception;
289            } catch (Throwable throwable) {
290                throwable.printStackTrace();
291                throw new BuildException(throwable);
292            }
293        }
294    }
295}