package com.laytonsmith.PureUtilities.ClassLoading.Annotations;
import com.laytonsmith.PureUtilities.ClassLoading.ClassDiscovery;
import com.laytonsmith.PureUtilities.ClassLoading.ClassDiscoveryCache;
import com.laytonsmith.PureUtilities.ClassLoading.ClassDiscoveryURLCache;
import com.laytonsmith.PureUtilities.Common.Annotations.AnnotationChecks;
import com.laytonsmith.PureUtilities.Common.StreamUtils;
import com.laytonsmith.PureUtilities.Common.StringUtils;
import com.laytonsmith.abstraction.Implementation;
import com.laytonsmith.annotations.api;
import com.laytonsmith.annotations.typeof;
import com.laytonsmith.core.SimpleDocumentation;
import com.laytonsmith.core.functions.DummyFunction;
import com.laytonsmith.core.natives.interfaces.TypeofRunnerFor;
import com.laytonsmith.core.natives.interfaces.TypeofRunnerIface;
import java.io.File;
import java.io.FileOutputStream;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
*
*/
public class CacheAnnotations {
public static void main(String[] args) throws Exception {
File outputDir = new File(args[0]);
File scanDir = new File(args[1]);
if(outputDir.toString().startsWith("-classpath") || outputDir.toString().startsWith("-Xdebug")){
//This happens when running locally. I dunno what that is, but we
//can skip this step.
StreamUtils.GetSystemOut().println("Skipping annotation caching, running locally.");
return;
}
StreamUtils.GetSystemOut().println("-- Caching annotations --");
StreamUtils.GetSystemOut().println("Scanning for classes in " + scanDir.getAbsolutePath());
StreamUtils.GetSystemOut().println("Outputting file to directory " + outputDir.getAbsolutePath());
long start = System.currentTimeMillis();
URL cacheFile = new URL("file:" + scanDir.getCanonicalPath());
ClassDiscoveryURLCache cache = new ClassDiscoveryURLCache(cacheFile);
cache.writeDescriptor(new FileOutputStream(new File(outputDir, ClassDiscoveryCache.OUTPUT_FILENAME)));
StreamUtils.GetSystemOut().println("Done writing " + ClassDiscoveryCache.OUTPUT_FILENAME + ", which took " + (System.currentTimeMillis() - start) + " ms.");
ClassDiscovery.getDefaultInstance().addPreCache(cacheFile, cache);
ClassDiscovery.getDefaultInstance().addDiscoveryLocation(cacheFile);
StreamUtils.GetSystemOut().println("-- Checking for custom compile errors --");
AnnotationChecks.checkForceImplementation();
Implementation.setServerType(Implementation.Type.SHELL);
List<String> uhohs = new ArrayList<>();
Set<Class> apiClasses = new HashSet<>();
apiClasses.addAll(ClassDiscovery.getDefaultInstance().loadClassesWithAnnotation(api.class));
apiClasses.addAll(ClassDiscovery.getDefaultInstance().loadClassesWithAnnotation(typeof.class));
if(apiClasses.isEmpty()){
// Sanity check
throw new Exception("API classes should not be empty");
}
for(Class c : apiClasses){
boolean isGetNameExempt = false;
if(c.isInterface()){
for(Class r : ClassDiscovery.getDefaultInstance().loadClassesWithAnnotation(TypeofRunnerFor.class)){
TypeofRunnerFor f = (TypeofRunnerFor) r.getAnnotation(TypeofRunnerFor.class);
if(f.value() == c){
isGetNameExempt = c.getAnnotation(typeof.class) != null;
c = r;
break;
}
}
}
// Verify that all classes that are @api classes have the valid functions required for proper documentation
// generation, as well as ultimately extend at minimum SimpleDocumentation.
if(DummyFunction.class.isAssignableFrom(c)){
// Skip this one. These are excused from the normal reporting requirements.
continue;
}
if(!SimpleDocumentation.class.isAssignableFrom(c) && !TypeofRunnerIface.class.isAssignableFrom(c)){
uhohs.add(c.getName() + " must implement SimpleDocumentation");
continue;
}
for(Method m : SimpleDocumentation.class.getDeclaredMethods()){
try {
c.getDeclaredMethod(m.getName(), m.getParameterTypes());
} catch (NoSuchMethodException ex) {
// typeof is exempt from having getName in each individual class, because the
// typeof value is that information.
if(!m.getName().equals("getName")){
if(c.getAnnotation(typeof.class) != null && !isGetNameExempt){
uhohs.add(c.getName() + " must implement " + m.getName() + "().");
}
}
} catch (SecurityException ex) {
throw new Error(ex);
}
}
}
if(!uhohs.isEmpty()){
Collections.sort(uhohs);
throw new Exception("There " + StringUtils.PluralHelper(uhohs.size(), "compile error") + ":\n" + StringUtils.Join(uhohs, "\n"));
}
StreamUtils.GetSystemOut().println("-- Finished with custom compiler checks --");
}
}