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