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