166 lines
6.1 KiB
Java
166 lines
6.1 KiB
Java
package speiger.src.builder.processor;
|
|
|
|
import java.io.IOException;
|
|
import java.nio.file.Files;
|
|
import java.nio.file.Path;
|
|
import java.util.Collections;
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
import java.util.concurrent.Executors;
|
|
import java.util.concurrent.ThreadPoolExecutor;
|
|
import java.util.concurrent.TimeUnit;
|
|
import java.util.concurrent.atomic.AtomicLong;
|
|
import java.util.function.Consumer;
|
|
import java.util.stream.Collectors;
|
|
|
|
import speiger.src.builder.base.Template;
|
|
import speiger.src.builder.mappers.IMapper;
|
|
import speiger.src.builder.misc.FileUtils;
|
|
|
|
public abstract class TemplateProcessor
|
|
{
|
|
Path sourceFolder;
|
|
Path outputFolder;
|
|
Path dataFolder;
|
|
boolean silencedSuccess;
|
|
boolean init = false;
|
|
|
|
public TemplateProcessor(Path sourceFolder, Path outputFolder, Path dataFolder)
|
|
{
|
|
this(false, sourceFolder, outputFolder, dataFolder);
|
|
}
|
|
|
|
public TemplateProcessor(boolean silencedSuccess, Path sourceFolder, Path outputFolder, Path dataFolder)
|
|
{
|
|
this.silencedSuccess = silencedSuccess;
|
|
this.sourceFolder = sourceFolder;
|
|
this.outputFolder = outputFolder;
|
|
this.dataFolder = dataFolder;
|
|
}
|
|
|
|
/**
|
|
* Function that is called before the initial run of the template processor.
|
|
* It is only called once to basically initialize the program and then run as needed.
|
|
*/
|
|
protected abstract void init();
|
|
|
|
/**
|
|
* Function that decides which file is going to be used and which not. Basically black/white list as needed
|
|
* @param fileName FileName without folder structure. Since this is designed to have 0 duplicated file names.
|
|
* @return true if it is valid to be processed or false if it isn't
|
|
*/
|
|
protected abstract boolean isFileValid(Path fileName);
|
|
|
|
/**
|
|
* Main function that processes a filename to its target
|
|
* @param fileName name of the file that should be processed without the type of file. (.template or .json etc etc)
|
|
* @param process all modifications to the file that should be executed
|
|
*/
|
|
public abstract void createProcesses(String fileName, Consumer<TemplateProcess> process);
|
|
|
|
/**
|
|
* Function that asks if the folder structure on the source folder should be kept or not
|
|
* @return true if the source-structure should be kept or flattened.
|
|
*/
|
|
protected abstract boolean relativePackages();
|
|
|
|
/**
|
|
* Function that tests if a Mapper was used or not. Useful for small scale projects that barely have any mappers
|
|
* but sadly more like to cause not needed warnings for large scale projects.
|
|
* @return true if it should be enabled
|
|
*/
|
|
protected abstract boolean debugUnusedMappers();
|
|
|
|
/**
|
|
* Function called before the Template Processor runs.
|
|
* But after init is called and All files are loaded in and the task queue is already ready.
|
|
*/
|
|
protected void beforeStart() {}
|
|
|
|
/**
|
|
* Function that is called while the template generation is running and all after the File hashes for the memory are being generated.
|
|
* If you have anything to do while the processor is running this would be the ideal time to do it.
|
|
*/
|
|
protected void whileProcessing() {}
|
|
|
|
/**
|
|
* Function that is being called after the template processor is called and after the file cache has been updated
|
|
* Basically just before "Saved Changes" is called.
|
|
*/
|
|
protected void afterFinish() {}
|
|
|
|
protected Path getSourceFolder() { return sourceFolder; }
|
|
protected Path getOutputFolder() { return outputFolder; }
|
|
protected Path getDataFolder() { return dataFolder; }
|
|
protected boolean isSilencedSuccess() { return silencedSuccess; }
|
|
|
|
@SuppressWarnings("unchecked")
|
|
public final boolean process(boolean force) throws IOException, InterruptedException
|
|
{
|
|
if(!init)
|
|
{
|
|
init = true;
|
|
init();
|
|
}
|
|
Map<String, String> existing = FileUtils.loadMappings(dataFolder);
|
|
List<Path> pathsLeft = Files.walk(sourceFolder).filter(Files::isRegularFile).filter(T -> isFileValid(T.getFileName())).filter(T -> force || FileUtils.isValid(T, existing)).collect(Collectors.toList());
|
|
if(pathsLeft.isEmpty() && !force)
|
|
{
|
|
System.out.println("Nothing has changed");
|
|
return false;
|
|
}
|
|
AtomicLong[] counters = new AtomicLong[]{new AtomicLong(), new AtomicLong(), new AtomicLong()};
|
|
final boolean relative = relativePackages();
|
|
Set<IMapper>[] mappers = debugUnusedMappers() ? new Set[]{Collections.synchronizedSet(new HashSet<>()), Collections.synchronizedSet(new HashSet<>())} : null;
|
|
ThreadPoolExecutor service = (ThreadPoolExecutor)Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
|
|
service.setKeepAliveTime(10, TimeUnit.MILLISECONDS);
|
|
service.allowCoreThreadTimeOut(true);
|
|
beforeStart();
|
|
service.submit(() -> {
|
|
for(int i = 0,m=pathsLeft.size();i<m;i++)
|
|
{
|
|
Path path = pathsLeft.get(i);
|
|
try
|
|
{
|
|
long startTime = System.currentTimeMillis();
|
|
Template template = Template.parse(path);
|
|
counters[2].addAndGet(System.currentTimeMillis() - startTime);
|
|
counters[1].addAndGet(1);
|
|
createProcesses(FileUtils.getFileName(path), T -> {service.execute(new BuildTask(relative ? outputFolder.resolve(sourceFolder.relativize(path).getParent()) : outputFolder, template, T, silencedSuccess, mappers));counters[0].addAndGet(1);});
|
|
}
|
|
catch(Exception e)
|
|
{
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
});
|
|
long start = System.currentTimeMillis();
|
|
System.out.println("Started Tasks");
|
|
for(int i = 0,m=pathsLeft.size();i<m;i++)
|
|
{
|
|
Path path = pathsLeft.get(i);
|
|
existing.put(FileUtils.getFileName(path), FileUtils.getMD5String(path));
|
|
}
|
|
whileProcessing();
|
|
while(service.getActiveCount() > 0)
|
|
{
|
|
Thread.sleep(10);
|
|
}
|
|
if(mappers != null && mappers[0].size() != mappers[1].size())
|
|
{
|
|
mappers[0].removeAll(mappers[1]);
|
|
for(IMapper mapper : mappers[0])
|
|
{
|
|
System.out.println("Mapper ["+mapper.getSearchValue()+"] is not used in the Entire Build Process");
|
|
}
|
|
}
|
|
System.out.println("Finished Building ["+counters[0].get()+"] Files from ["+counters[1].get()+"] in "+(System.currentTimeMillis() - start)+"ms (Template Parsing: "+counters[2].get()+"ms (avg: "+((counters[2].get() / Math.max(1D, counters[1].get())))+"ms)");
|
|
FileUtils.saveMappings(existing, dataFolder);
|
|
afterFinish();
|
|
System.out.println("Saved Changes");
|
|
return true;
|
|
}
|
|
}
|