001package ball.annotation.processing;
002/*-
003 * ##########################################################################
004 * Utilities
005 * $Id: AntLibProcessor.java 6202 2020-06-16 13:44:11Z ball $
006 * $HeadURL: svn+ssh://svn.hcf.dev/var/spool/scm/repository.svn/ball-util/trunk/src/main/java/ball/annotation/processing/AntLibProcessor.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.annotation.ServiceProviderFor;
024import ball.util.ant.taskdefs.AntLib;
025import ball.util.ant.taskdefs.AntTask;
026import ball.xml.FluentDocument;
027import ball.xml.FluentDocumentBuilderFactory;
028import ball.xml.XalanConstants;
029import java.io.OutputStream;
030import java.util.Set;
031import java.util.TreeMap;
032import java.util.TreeSet;
033import javax.annotation.processing.Processor;
034import javax.tools.FileObject;
035import javax.tools.JavaFileManager;
036import javax.xml.transform.Transformer;
037import javax.xml.transform.TransformerFactory;
038import javax.xml.transform.dom.DOMSource;
039import javax.xml.transform.stream.StreamResult;
040import lombok.NoArgsConstructor;
041import lombok.ToString;
042import org.apache.tools.ant.Task;
043
044import static java.lang.reflect.Modifier.isAbstract;
045import static javax.tools.StandardLocation.CLASS_OUTPUT;
046import static javax.xml.transform.OutputKeys.INDENT;
047import static javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION;
048
049/**
050 * Generates {@code antlib.xml} (at location(s) specified by {@link AntLib})
051 * at the end of annotation processing.
052 *
053 * @author {@link.uri mailto:ball@hcf.dev Allen D. Ball}
054 * @version $Revision: 6202 $
055 */
056@ServiceProviderFor({ Processor.class })
057@For({ AntLib.class, AntTask.class })
058@NoArgsConstructor @ToString
059public class AntLibProcessor extends AnnotatedProcessor
060                             implements ClassFileProcessor, XalanConstants {
061    private static final String ANTLIB_XML = "antlib.xml";
062
063    private static final Transformer TRANSFORMER;
064
065    static {
066        try {
067            TRANSFORMER = TransformerFactory.newInstance().newTransformer();
068            TRANSFORMER.setOutputProperty(OMIT_XML_DECLARATION, NO);
069            TRANSFORMER.setOutputProperty(INDENT, YES);
070            TRANSFORMER.setOutputProperty(XALAN_INDENT_AMOUNT.toString(),
071                                          String.valueOf(2));
072        } catch (Exception exception) {
073            throw new ExceptionInInitializerError(exception);
074        }
075    }
076
077    @Override
078    public void process(Set<Class<?>> set,
079                        JavaFileManager fm) throws Throwable {
080        TreeSet<String> paths = new TreeSet<>();
081        AntLibXML antlib = new AntLibXML();
082
083        for (Class<?> type : set) {
084            AntTask annotation = type.getAnnotation(AntTask.class);
085
086            if (annotation != null) {
087                if (Task.class.isAssignableFrom(type)) {
088                    if (! isAbstract(type.getModifiers())) {
089                        antlib.put(annotation.value(), type);
090                    }
091                }
092            }
093
094            AntLib lib = type.getAnnotation(AntLib.class);
095
096            if (lib != null) {
097                paths.add(type.getPackage().getName());
098            }
099        }
100
101        for (String path : paths) {
102            FileObject file =
103                fm.getFileForOutput(CLASS_OUTPUT, path, ANTLIB_XML, null);
104
105            try (OutputStream out = file.openOutputStream()) {
106                antlib.writeTo(out);
107            }
108        }
109    }
110
111    @NoArgsConstructor
112    private static class AntLibXML extends TreeMap<String,Class<?>> {
113        private static final long serialVersionUID = -8903476717502118017L;
114
115        public FluentDocument asDocument() throws Exception {
116            FluentDocument d =
117                FluentDocumentBuilderFactory.newInstance()
118                .newDocumentBuilder()
119                .newDocument();
120
121            d.add(d.element("antlib",
122                            entrySet().stream()
123                            .map(t -> d.element("taskdef",
124                                                d.attr("name", t.getKey()),
125                                                d.attr("classname",
126                                                       t.getValue().getName())))));
127
128            return d;
129        }
130
131        public void writeTo(OutputStream out) throws Exception {
132            TRANSFORMER.transform(new DOMSource(asDocument()),
133                                  new StreamResult(out));
134        }
135    }
136}