Uploading SourceCode and Documentation

This commit is contained in:
Speiger 2021-01-11 13:43:07 +01:00
parent ce515f07e4
commit a189a3cea4
17 changed files with 912 additions and 0 deletions

View File

@ -1,2 +1,45 @@
# Simple-Code-Generator
This Tool is a very Simple Code Generator that can read a template file and segment it based on a Set of variables that can be defined.
Separate to that this tool can replace text via simple Regular Expression mapping.
It has a cache that keeps track of the input (if it was changed), and only cares that the file format is Text based.
It is as bare bones as it can get but that also makes it flexible.
# How to create a Template Processor
Create a class that extends TemplateProcessor.
And run the process method
SourceFolder: Is the folder that is traversed through and Files are given back.
OutputFolder: Is the folder where the Processed files get put into. If the "relativePackages" are set to true then the source folder structure is transferred.
DataFolder: Is the folder where the input cache is stored. It uses a MD5 generator to compare inputs. Right now only FileNames without extensions are stored in there. So no Duplicated FileName support for now.
##### Methods:
init: Is called when the Processes was started for the first time.
isFileValid: The Method that checks if a File can be used for a template.
relativePackages: If the Folder Structure from the SourceFolder should be transferred to the OutputFolder.
createProcesses: The Main Function that provides the FileName (without extension) and a consumer of how the file should be processed.
A TemplateProcess is a Collection of Variables that is used for File Segmentation and a List of UnaryOperators that allow to edit the build code.
A Simple sum up: Template Process is Tuple<Set<String>, List<UnaryOperator>> that also contains the new FileName.
```java
package test.example;
public class ExampleClass
{
#if METHOD_ONE
public void methodOne() {}
#else if METHOD_TWO && !SKIP
public void methodTwo() {}
#else
public void methodThree() {}
#endif
}
```
If the Variable "METHOD_ONE" is present only the code in the if is parsed into the template process.
If the Variable "METHOD_TWO" is present and the "SKIP" Variable is missing then the code in the else if block is parsed into the template process
Otherwise the else block is included.
Whenever a if is started, it has to end with a #endif, Even if the last condition is a else if or a else.
Recursion is supported and necessary

View File

@ -0,0 +1,83 @@
package speiger.src.builder.base;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.StringJoiner;
import java.util.regex.Pattern;
import speiger.src.builder.conditions.ICondition;
public class ConditionedSegment
{
static final Pattern AND = Pattern.compile("(&&)");
static final Pattern OR = Pattern.compile("(||)");
int index;
List<Segment> segments = new ArrayList<>();
public ConditionedSegment(int index)
{
this.index = index;
}
public void addSegment(Segment segment)
{
segments.add(segment);
}
public int build(Set<String> parsePool, StringBuilder builder, int baseIndex)
{
baseIndex += index;
int length = builder.length();
for(int i = 0,m=segments.size();i<m;i++)
{
if(segments.get(i).build(parsePool, builder, baseIndex)) break;
}
return builder.length() - length;
}
public static int parse(String currentLine, List<String> lines, int currentIndex, int startIndex, List<ConditionedSegment> segments) throws IllegalStateException
{
ConditionedSegment segment = new ConditionedSegment(startIndex);
ICondition condition = ICondition.parse(currentLine);
List<ConditionedSegment> childSegments = new ArrayList<>();
StringJoiner segmentText = new StringJoiner("\n", "\n", "");
for(int i = currentIndex+1;i<lines.size();i++)
{
String s = lines.get(i);
String trimmed = s.trim();
if(trimmed.startsWith("#"))
{
if(trimmed.startsWith("#else if"))
{
segment.addSegment(new Segment(segmentText.toString(), condition, childSegments));
condition = ICondition.parse(trimmed.substring(8).trim());
childSegments = new ArrayList<>();
segmentText = new StringJoiner("\n", "\n", "");
}
else if(trimmed.startsWith("#else"))
{
segment.addSegment(new Segment(segmentText.toString(), condition, childSegments));
condition = ICondition.ALWAYS_TRUE;
childSegments = new ArrayList<>();
segmentText = new StringJoiner("\n", "\n", "");
}
else if(trimmed.startsWith("#endif"))
{
segment.addSegment(new Segment(segmentText.toString(), condition, childSegments));
segments.add(segment);
return i - currentIndex;
}
else if(trimmed.startsWith("#if"))
{
i += parse(trimmed.substring(3).trim(), lines, i, segmentText.length(), childSegments);
}
continue;
}
segmentText.add(s);
}
throw new IllegalStateException("Unclosed #If found!");
}
}

View File

@ -0,0 +1,35 @@
package speiger.src.builder.base;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import speiger.src.builder.conditions.ICondition;
public class Segment
{
ICondition condition;
String text;
List<ConditionedSegment> segments = new ArrayList<>();
public Segment(String text, ICondition condition, List<ConditionedSegment> segments)
{
this.text = text;
this.condition = condition;
this.segments = segments;
}
public boolean build(Set<String> parsePool, StringBuilder builder, int index)
{
if(condition.isValid(parsePool))
{
builder.insert(index, text);
for(int i = 0,offset=0,m=segments.size();i<m;i++)
{
offset += segments.get(i).build(parsePool, builder, index+offset);
}
return true;
}
return false;
}
}

View File

@ -0,0 +1,72 @@
package speiger.src.builder.base;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.StringJoiner;
import java.util.function.UnaryOperator;
import speiger.src.builder.misc.FileUtils;
public class Template
{
String fileName;
String textFile;
List<ConditionedSegment> segments;
public Template(String fileName, String textFile, List<ConditionedSegment> segments)
{
this.fileName = fileName;
this.textFile = textFile;
this.segments = segments;
}
public String getFileName()
{
return fileName;
}
public String build(Set<String> parsePool, List<UnaryOperator<String>> mappers)
{
StringBuilder builder = new StringBuilder(textFile);
for(int i = 0,offset=0,m=segments.size();i<m;i++)
{
offset += segments.get(i).build(parsePool, builder, offset);
}
String result = builder.toString();
for(int i = 0,m=mappers.size();i<m;i++)
{
result = mappers.get(i).apply(result);
}
return result;
}
public static Template parse(Path file) throws IOException
{
List<ConditionedSegment> segments = new ArrayList<ConditionedSegment>();
StringJoiner joiner = new StringJoiner("\n");
List<String> lines = Files.readAllLines(file);
for(int i = 0;i<lines.size();i++)
{
String s = lines.get(i);
String trimmed = s.trim();
if(trimmed.startsWith("#"))
{
if(trimmed.startsWith("#if"))
{
i += ConditionedSegment.parse(s.trim().substring(3).trim(), lines, i, joiner.length(), segments);
continue;
}
else if(trimmed.startsWith("#symlink"))
{
return Template.parse(file.getParent().resolve(trimmed.substring(8).trim()));
}
}
joiner.add(s);
}
return new Template(FileUtils.getFileName(file.getFileName()), joiner.toString(), segments);
}
}

View File

@ -0,0 +1,33 @@
package speiger.src.builder.conditions;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
public class AndCondition implements ICondition
{
List<ICondition> conditions = new ArrayList<>();
public AndCondition(ICondition base)
{
conditions.add(base);
}
public void addCondition(ICondition e)
{
conditions.add(e);
}
@Override
public boolean isValid(Set<String> parsePool)
{
for(int i = 0,m=conditions.size();i<m;i++)
{
if(!conditions.get(i).isValid(parsePool))
{
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,21 @@
package speiger.src.builder.conditions;
import java.util.Set;
public class FlagCondition implements ICondition
{
String flag;
boolean inverted;
public FlagCondition(String flag, boolean inverted)
{
this.flag = flag;
this.inverted = inverted;
}
@Override
public boolean isValid(Set<String> parsePool)
{
return parsePool.contains(flag) != inverted;
}
}

View File

@ -0,0 +1,61 @@
package speiger.src.builder.conditions;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
public interface ICondition
{
public static final ICondition ALWAYS_TRUE = T -> true;
public boolean isValid(Set<String> parsePool);
public static ICondition parse(String condition)
{
String[] elements = condition.split(" ");
List<ICondition> conditions = new ArrayList<ICondition>();
for(int i = 0;i<elements.length;i++)
{
if(elements[i].equalsIgnoreCase("&&"))
{
if(i==elements.length-1)
{
continue;
}
if(condition.isEmpty())
{
conditions.add(new AndCondition(parseSimpleCondition(elements[++i])));
}
else
{
ICondition con = conditions.get(conditions.size() - 1);
if(con instanceof AndCondition)
{
((AndCondition)con).addCondition(parseSimpleCondition(elements[++i]));
}
else
{
AndCondition replacement = new AndCondition(con);
replacement.addCondition(parseSimpleCondition(elements[++i]));
conditions.set(conditions.size()-1, replacement);
}
}
}
else if(!elements[i].equalsIgnoreCase("||"))
{
conditions.add(parseSimpleCondition(elements[i]));
}
}
switch(conditions.size())
{
case 0: return ALWAYS_TRUE;
case 1: return conditions.get(0);
default: return new OrCondition(conditions);
}
}
static ICondition parseSimpleCondition(String s)
{
return s.startsWith("!") ? new FlagCondition(s.substring(1), true) : new FlagCondition(s, false);
}
}

View File

@ -0,0 +1,27 @@
package speiger.src.builder.conditions;
import java.util.List;
import java.util.Set;
public class OrCondition implements ICondition
{
List<ICondition> conditions;
public OrCondition(List<ICondition> conditions)
{
this.conditions = conditions;
}
@Override
public boolean isValid(Set<String> parsePool)
{
for(int i = 0,m=conditions.size();i<m;i++)
{
if(conditions.get(i).isValid(parsePool))
{
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,64 @@
package speiger.src.builder.mappers;
import java.util.function.UnaryOperator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import speiger.src.builder.misc.RegexUtil;
public class ArgumentMapper implements UnaryOperator<String>
{
Pattern pattern;
String replacement;
String argumentBreaker;
String braces = "()";
boolean removeBraces;
public ArgumentMapper(String pattern, String replacement, String argumentBreaker)
{
this.pattern = Pattern.compile(pattern);
this.replacement = replacement;
this.argumentBreaker = argumentBreaker;
}
public ArgumentMapper setBraceType(String s)
{
if(s.length() != 2) throw new IllegalStateException("Start and End char only");
braces = s;
return this;
}
public ArgumentMapper removeBraces()
{
removeBraces = true;
return this;
}
@Override
public String apply(String t)
{
Matcher matcher = pattern.matcher(t);
if(matcher.find())
{
StringBuffer buffer = new StringBuffer();
do
{
String text = RegexUtil.searchUntil(t, matcher.end()-1, braces.charAt(0), braces.charAt(1));
if(!text.isEmpty())
{
RegexUtil.skip(matcher.appendReplacement(buffer, ""), text.length());
buffer.append(String.format(replacement, (Object[])getString(text).split(argumentBreaker)));
}
}
while(matcher.find());
matcher.appendTail(buffer);
return buffer.toString();
}
return t;
}
protected String getString(String s)
{
return removeBraces ? s.substring(1, s.length() - 1) : s;
}
}

View File

@ -0,0 +1,61 @@
package speiger.src.builder.mappers;
import java.util.function.UnaryOperator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import speiger.src.builder.misc.RegexUtil;
public class InjectMapper implements UnaryOperator<String>
{
Pattern pattern;
String replacement;
String braces = "()";
boolean removeBraces;
public InjectMapper(String pattern, String replacement)
{
this.pattern = Pattern.compile(pattern);
this.replacement = replacement;
}
public InjectMapper setBraceType(String s)
{
if(s.length() != 2) throw new IllegalStateException("Start and End char only");
braces = s;
return this;
}
public InjectMapper removeBraces()
{
removeBraces = true;
return this;
}
@Override
public String apply(String t)
{
Matcher matcher = pattern.matcher(t);
if(matcher.find())
{
StringBuffer buffer = new StringBuffer();
do
{
String text = RegexUtil.searchUntil(t, matcher.end()-1, braces.charAt(0), braces.charAt(1));
if(!text.isEmpty())
{
RegexUtil.skip(matcher.appendReplacement(buffer, ""), text.length());
buffer.append(String.format(replacement, getString(text)));
}
} while (matcher.find());
matcher.appendTail(buffer);
return buffer.toString();
}
return t;
}
protected String getString(String s)
{
return removeBraces ? s.substring(1, s.length() - 1) : s;
}
}

View File

@ -0,0 +1,43 @@
package speiger.src.builder.mappers;
import java.util.function.UnaryOperator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import speiger.src.builder.misc.RegexUtil;
public class LineMapper implements UnaryOperator<String>
{
Pattern pattern;
public LineMapper(String pattern)
{
this.pattern = Pattern.compile(pattern, Pattern.LITERAL);
}
@Override
public String apply(String t)
{
Matcher matcher = pattern.matcher(t);
if(matcher.find())
{
StringBuffer buffer = new StringBuffer();
do
{
int start = matcher.end() - 1;
int[] result = RegexUtil.findFullLine(t, start);
if(result != null)
{
matcher.appendReplacement(buffer, pattern.pattern());
buffer.setLength(buffer.length() - (start - result[0]));
RegexUtil.skip(matcher, result[1] - start);
}
} while (matcher.find());
matcher.appendTail(buffer);
return buffer.toString();
}
return t;
}
}

View File

@ -0,0 +1,22 @@
package speiger.src.builder.mappers;
import java.util.function.UnaryOperator;
import java.util.regex.Pattern;
public class SimpleMapper implements UnaryOperator<String>
{
Pattern pattern;
String replacement;
public SimpleMapper(String pattern, String replacement)
{
this.pattern = Pattern.compile(pattern, Pattern.LITERAL);
this.replacement = replacement;
}
@Override
public String apply(String t)
{
return pattern.matcher(t).replaceAll(replacement);
}
}

View File

@ -0,0 +1,88 @@
package speiger.src.builder.misc;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.MessageDigest;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
public class FileUtils
{
public static boolean isValid(Path path, Map<String, String> existing)
{
String s = existing.get(getFileName(path));
return s == null || !s.equals(FileUtils.getMD5String(path));
}
public static Map<String, String> loadMappings(Path dataFolder) throws IOException
{
Map<String, String> result = new LinkedHashMap<>();
dataFolder = dataFolder.resolve("cache.bin");
if(Files.exists(dataFolder))
{
for(String s : Files.readAllLines(dataFolder))
{
String[] array = s.split("=");
if(array.length == 2)
{
result.put(array[0], array[1]);
}
}
}
return result;
}
public static void saveMappings(Map<String, String> mappings, Path dataFolder)
{
try(BufferedWriter writer = Files.newBufferedWriter(dataFolder.resolve("cache.bin")))
{
for(Entry<String, String> entry : mappings.entrySet())
{
writer.write(entry.getKey()+"="+entry.getValue());
writer.newLine();
}
writer.flush();
}
catch(Exception e)
{
e.printStackTrace();
}
}
public static String getFileName(Path path)
{
String name = path.getFileName().toString();
int index = name.indexOf(".");
return index == -1 ? name : name.substring(0, index);
}
public static String getMD5String(Path path)
{
try(InputStream stream = Files.newInputStream(path))
{
MessageDigest digest = MessageDigest.getInstance("MD5");
byte[] byteArray = new byte[2048];
int bytesCount = 0;
while((bytesCount = stream.read(byteArray)) != -1)
{
digest.update(byteArray, 0, bytesCount);
}
byte[] bytes = digest.digest();
StringBuilder sb = new StringBuilder();
for(int i = 0;i < bytes.length;i++)
{
sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1));
}
return sb.toString();
}
catch(Exception e)
{
return null;
}
}
}

View File

@ -0,0 +1,77 @@
package speiger.src.builder.misc;
import java.lang.reflect.Field;
import java.util.regex.Matcher;
public class RegexUtil
{
static Field LAST_POS;
public static Matcher skip(Matcher matcher, int amount)
{
try
{
LAST_POS.setInt(matcher, LAST_POS.getInt(matcher) + amount);
}
catch(Exception e)
{
e.printStackTrace();
}
return matcher;
}
public static String searchUntil(String text, int startIndex, char increase, char decrease)
{
if(text.charAt(startIndex + 1) != increase)
{
return "";
}
int inc = 0;
StringBuilder builder = new StringBuilder();
for(int i = startIndex + 1;i<text.length();i++)
{
char current = text.charAt(i);
if(current == '\\' && i < text.length() - 1 && text.charAt(i+1) == 'n')
{
return "";
}
else if(current == increase)
{
inc++;
}
else if(current == decrease)
{
inc--;
if(inc <= 0)
{
return builder.append(decrease).toString();
}
}
builder.append(current);
}
return "";
}
public static int[] findFullLine(String s, int startIndex)
{
int offset = s.indexOf("\n", startIndex);
if(offset == -1) return null;
int start = s.lastIndexOf("\n", startIndex);
if(start == -1) return null;
return new int[]{start, offset};
}
static
{
try
{
Field field = Matcher.class.getDeclaredField("lastAppendPosition");
field.setAccessible(true);
LAST_POS = field;
}
catch(Exception e)
{
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,46 @@
package speiger.src.builder.processor;
import java.io.BufferedWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import speiger.src.builder.base.Template;
public class BuildTask implements Runnable
{
Path basePath;
Template template;
TemplateProcess process;
public BuildTask(Path basePath, Template template, TemplateProcess process)
{
this.basePath = basePath;
this.template = template;
this.process = process;
}
@Override
public void run()
{
String s = template.build(process.parsePool, process.mappers);
Path path = (process.pathBuilder != null ? process.pathBuilder.apply(basePath) : basePath).resolve(process.fileName);
try
{
Files.createDirectories(path.getParent());
}
catch(Exception e)
{
}
try(BufferedWriter writer = Files.newBufferedWriter(path))
{
writer.write(s);
writer.flush();
System.out.println("Created: "+process.fileName);
}
catch(Exception e)
{
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,48 @@
package speiger.src.builder.processor;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.UnaryOperator;
public class TemplateProcess
{
UnaryOperator<Path> pathBuilder;
String fileName;
Set<String> parsePool = new HashSet<>();
List<UnaryOperator<String>> mappers = new ArrayList<>();
public TemplateProcess(String fileName)
{
this.fileName = fileName;
}
public void setPathBuilder(UnaryOperator<Path> pathBuilder)
{
this.pathBuilder = pathBuilder;
}
public void addFlags(String...flags)
{
parsePool.addAll(Arrays.asList(flags));
}
public void addFlags(Collection<String> flags)
{
parsePool.addAll(flags);
}
public void addMapper(UnaryOperator<String> mapper)
{
mappers.add(mapper);
}
public void addMappers(Collection<UnaryOperator<String>> mappers)
{
this.mappers.addAll(mappers);
}
}

View File

@ -0,0 +1,88 @@
package speiger.src.builder.processor;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import speiger.src.builder.base.Template;
import speiger.src.builder.misc.FileUtils;
public abstract class TemplateProcessor
{
Path sourceFolder;
Path outputFolder;
Path dataFolder;
boolean init = false;
public TemplateProcessor(Path sourceFolder, Path outputFolder, Path dataFolder)
{
this.sourceFolder = sourceFolder;
this.outputFolder = outputFolder;
this.dataFolder = dataFolder;
}
protected abstract void init();
protected abstract boolean isFileValid(Path fileName);
public abstract void createProcesses(String fileName, Consumer<TemplateProcess> process);
protected abstract boolean relativePackages();
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;
}
final boolean relative = relativePackages();
ThreadPoolExecutor service = (ThreadPoolExecutor)Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
service.setKeepAliveTime(10, TimeUnit.MILLISECONDS);
service.allowCoreThreadTimeOut(true);
service.submit(() -> {
for(int i = 0,m=pathsLeft.size();i<m;i++)
{
Path path = pathsLeft.get(i);
try
{
Template template = Template.parse(path);
createProcesses(FileUtils.getFileName(path), T -> service.execute(new BuildTask(relative ? outputFolder.resolve(sourceFolder.relativize(path).getParent()) : outputFolder, template, T)));
}
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));
}
while(service.getActiveCount() > 0)
{
Thread.sleep(10);
}
System.out.println("Finished Tasks: "+(System.currentTimeMillis() - start)+"ms");
FileUtils.saveMappings(existing, dataFolder);
System.out.print("Saved Changes");
return true;
}
}