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