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.annotation.processing.ClassFileProcessor; 022import java.io.File; 023import java.net.URLClassLoader; 024import java.util.ArrayList; 025import java.util.HashSet; 026import java.util.List; 027import java.util.stream.Stream; 028import javax.tools.StandardJavaFileManager; 029import javax.tools.ToolProvider; 030import lombok.Getter; 031import lombok.NoArgsConstructor; 032import lombok.Setter; 033import lombok.ToString; 034import lombok.experimental.Accessors; 035import org.apache.tools.ant.BuildException; 036import org.apache.tools.ant.Task; 037import org.apache.tools.ant.types.Path; 038import org.apache.tools.ant.util.ClasspathUtils; 039 040import static java.lang.reflect.Modifier.isAbstract; 041import static java.util.Arrays.asList; 042import static java.util.stream.Collectors.toList; 043import static javax.tools.StandardLocation.CLASS_OUTPUT; 044import static javax.tools.StandardLocation.CLASS_PATH; 045import static javax.tools.StandardLocation.PLATFORM_CLASS_PATH; 046import static javax.tools.StandardLocation.SOURCE_PATH; 047 048/** 049 * {@link.uri http://ant.apache.org/ Ant} {@link Task} to bootstrap 050 * {@link javax.annotation.processing.Processor}s. Creates and invokes 051 * {@link ClassFileProcessor}s found on the class path. 052 * 053 * {@ant.task} 054 * 055 * @author {@link.uri mailto:ball@hcf.dev Allen D. Ball} 056 */ 057@AntTask("process-class-files") 058@NoArgsConstructor @ToString 059public class ProcessClassFilesTask extends Task implements AnnotatedAntTask, ClasspathDelegateAntTask { 060 @Getter @Setter @Accessors(chain = true, fluent = true) 061 private ClasspathUtils.Delegate delegate = null; 062 @Getter @Setter 063 private File basedir = null; 064 @Getter 065 private Path srcPath = null; 066 @Getter @Setter 067 private File destdir = null; 068 069 private StandardJavaFileManager fm = null; 070 071 public void setSrcdir(Path srcdir) { 072 if (srcPath == null) { 073 srcPath = srcdir; 074 } else { 075 srcPath.append(srcdir); 076 } 077 } 078 079 public Path createSrc() { 080 if (srcPath == null) { 081 srcPath = new Path(getProject()); 082 } 083 084 return srcPath.createPath(); 085 } 086 087 @Override 088 public void init() throws BuildException { 089 super.init(); 090 ClasspathDelegateAntTask.super.init(); 091 092 try { 093 fm = ToolProvider.getSystemJavaCompiler().getStandardFileManager(null, null, null); 094 } catch (BuildException exception) { 095 throw exception; 096 } catch (RuntimeException exception) { 097 throw exception; 098 } catch (Throwable throwable) { 099 throwable.printStackTrace(); 100 throw new BuildException(throwable); 101 } 102 } 103 104 @Override 105 public void execute() throws BuildException { 106 super.execute(); 107 AnnotatedAntTask.super.execute(); 108 109 try { 110 if (getBasedir() == null) { 111 setBasedir(getProject().resolveFile(".")); 112 } 113 114 if (getDestdir() == null) { 115 setDestdir(getBasedir()); 116 } 117 118 List<File> srcPaths = 119 Stream.of(createSrc().list()) 120 .map(File::new) 121 .collect(toList()); 122 List<File> classPaths = 123 Stream.of(delegate.getClasspath().list()) 124 .map(File::new) 125 .collect(toList()); 126 127 fm.setLocation(SOURCE_PATH, srcPaths); 128 fm.setLocation(PLATFORM_CLASS_PATH, classPaths); 129 fm.setLocation(CLASS_PATH, classPaths); 130 fm.setLocation(CLASS_OUTPUT, asList(getDestdir())); 131 132 ClassLoader loader = fm.getClassLoader(CLASS_PATH); 133 134 if (loader instanceof URLClassLoader) { 135 loader = 136 URLClassLoader.newInstance(((URLClassLoader) loader).getURLs(), 137 getClass().getClassLoader()); 138 } 139 140 HashSet<Class<?>> types = new HashSet<>(); 141 List<Class<? extends ClassFileProcessor>> processors = new ArrayList<>(); 142 HashSet<String> names = new HashSet<>(); 143 144 for (String name : ClassFileProcessor.list(fm)) { 145 try { 146 Class<?> type = Class.forName(name, true, loader); 147 148 types.add(type); 149 150 if (! isAbstract(type.getModifiers())) { 151 if (ClassFileProcessor.class.isAssignableFrom(type)) { 152 processors.add(type.asSubclass(ClassFileProcessor.class)); 153 } 154 } 155 } catch (Throwable throwable) { 156 names.add(name); 157 } 158 } 159 160 if (names.isEmpty()) { 161 for (Class<? extends ClassFileProcessor> processor : processors) { 162 processor 163 .getDeclaredConstructor().newInstance() 164 .process(types, fm); 165 } 166 } else { 167 throw new BuildException("Failed to load " + names); 168 } 169 } catch (BuildException exception) { 170 throw exception; 171 } catch (RuntimeException exception) { 172 throw exception; 173 } catch (Throwable throwable) { 174 throwable.printStackTrace(); 175 throw new BuildException(throwable); 176 } 177 } 178}