Skip to content

Commit

Permalink
Add TS code generation and Refactor Code (#16)
Browse files Browse the repository at this point in the history
* Extracts some code used for java/js generation into new module
* Prepares a first version of the typescript generator (incorrect result for now).
* Adds qudtlib-js as a submodule
* Fixes typo in package name
* Refactors constants generation
* Generate units, quantitykinds and prefixes in allunits.ts
* Fixes Bug in factor unit queries:
    * overspecification was possible
    * simple units could not be selected using exponent 1
    * Both issues are fixed and checked with unit tests.
* Improves factor unit matching algorithm
* Simplifies matched check
* Removes unused code
* Removes unnecessary isMatched check
* Removes unnecessary method
* Enables exact and lenient matching modes
* Refactors unit/qk/prefix access methods to return optional
* Adds 'required' unit/qk/prefix access methods that throw NotFoundExceptions
* Adds tests for search/maching modes
* Removes cluttering convenience methods
* Refactors Qudt.java and tests slightly
* Add scalingOf triples if missing in QUDT
* Updates submodule to use latest qudtlib-js/main
  • Loading branch information
fkleedorfer authored Oct 14, 2022
1 parent 2102f96 commit f9eeaea
Show file tree
Hide file tree
Showing 42 changed files with 1,487 additions and 722 deletions.
4 changes: 4 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[submodule "qudtlib-js"]
path = qudtlib-js
url = https://github.com/qudtlib/qudtlib-js.git
branch = main
4 changes: 3 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
<module>qudtlib-ingest-qudt</module>
<module>qudtlib-data-gen</module>
<module>qudtlib-vocab</module>
<module>qudtlib-common</module>
<module>qudtlib-common-rdf</module>
<module>qudtlib-common-codegen</module>
<module>qudtlib-data</module>
<module>qudtlib-init-rdf</module>
<module>qudtlib-main</module>
Expand All @@ -39,6 +40,7 @@
<module>qudtlib-init-hardcoded</module>
<module>qudtlib-test</module>
<module>qudtlib-example</module>
<module>qudtlib-js-gen</module>
</modules>


Expand Down
30 changes: 30 additions & 0 deletions qudtlib-common-codegen/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>qudtlib-java</artifactId>
<groupId>io.github.qudtlib</groupId>
<version>1.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>qudtlib-common-codegen</artifactId>

<dependencies>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
</dependency>
<dependency>
<groupId>io.github.qudtlib</groupId>
<artifactId>qudtlib-model</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package io.github.qudtlib.common;

import freemarker.core.Environment;
import freemarker.core.TemplateNumberFormat;
import freemarker.core.TemplateNumberFormatFactory;
import freemarker.template.TemplateModelException;
import freemarker.template.TemplateNumberModel;
import java.math.BigDecimal;
import java.util.Locale;

public class BigDecimalFormatterFactory extends TemplateNumberFormatFactory {
@Override
public TemplateNumberFormat get(String s, Locale locale, Environment environment) {
return new TemplateNumberFormat() {
@Override
public String formatToPlainText(TemplateNumberModel templateNumberModel)
throws TemplateModelException {
Number num = templateNumberModel.getAsNumber();
if (!(num instanceof BigDecimal)) {
throw new IllegalArgumentException(
"This formatter can only be used with BigDecimals but was asked to format a "
+ num.getClass());
}
BigDecimal bd = (BigDecimal) templateNumberModel.getAsNumber();
return bd.toString();
}

@Override
public boolean isLocaleBound() {
return false;
}

@Override
public String getDescription() {
return "Number format for BigDecimal using BigDecimal.toString()";
}
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package io.github.qudtlib.common;

import freemarker.core.Environment;
import freemarker.template.*;
import io.github.qudtlib.common.safenames.SafeStringMapper;
import io.github.qudtlib.constgen.Constant;
import io.github.qudtlib.model.LangString;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.regex.Pattern;

public abstract class CodeGen {

public static Configuration getFreemarkerConfiguration() {
return getFreemarkerConfiguration(Thread.currentThread().getContextClassLoader());
}

public static Configuration getFreemarkerConfiguration(ClassLoader classLoaderForTemplate) {
Objects.requireNonNull(classLoaderForTemplate);
Configuration cfg = new Configuration(Configuration.VERSION_2_3_31);
cfg.setClassLoaderForTemplateLoading(classLoaderForTemplate, "/");
cfg.setDefaultEncoding("UTF-8");
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
cfg.setCustomNumberFormats(Map.of("toString", new BigDecimalFormatterFactory()));
return cfg;
}

public static void generateFileFromTemplate(
Configuration config,
String templateFileClasspathUrl,
Map<String, Object> templateVars,
File outFile)
throws IOException, TemplateException {
Template template = config.getTemplate(templateFileClasspathUrl);
FileWriter out = new FileWriter(outFile, StandardCharsets.UTF_8);
Environment env = template.createProcessingEnvironment(templateVars, out);
env.setOutputEncoding(StandardCharsets.UTF_8.toString());
env.process();
}

public static Constant makeConstant(
Set<LangString> labels, String iri, SafeStringMapper constantNameMapper) {
String label = labels.stream().findFirst().map(LangString::getString).orElse("[no label]");
String iriLocalName = iri.replaceAll("^.+[/|#]", "");
String codeConstantName = constantNameMapper.applyMapping(iriLocalName);
return new Constant(codeConstantName, iriLocalName, label);
}

public static SafeStringMapper javaConstantMapper() {
return new SafeStringMapper(javaConstantNameMapper);
}

static final Function<String, String> javaConstantNameMapper =
constName -> {
Pattern startPattern = Pattern.compile("^[$€a-zA-Z_]");
if (!startPattern.matcher(constName).lookingAt()) {
constName = "_" + constName;
}
constName = constName.replaceAll("-", "__");
return constName;
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package io.github.qudtlib.common.safenames;

public class NameCollisionException extends RuntimeException {
public NameCollisionException(String collidingInput, String input, String mappedOutput) {
super(
String.format(
"IdentifierCollision detected! Input String '%s' clashes with previously established Mapping '%s' => '%s'",
collidingInput, input, mappedOutput));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package io.github.qudtlib.common.safenames;

import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;

/**
* Applies the provided <b>deterministic</b> </b><code>mappingFunction</code> provided in the
* constructor, keeping track of the mappings produced. If two different inputs result in the same
* output, a {@link NameCollisionException} is thrown.
*/
public class SafeStringMapper {
private final Map<String, String> outputToInput = new ConcurrentHashMap<>();
private final Function<String, String> mappingFunction;

public SafeStringMapper(Function<String, String> mappingFunction) {
this.mappingFunction = mappingFunction;
}

/**
* Applies the specified mapping, throwing an exception if a previously produced output is
* reproduced with a different input.
*
* @param input the value to map safely
* @return the mapped value
*/
public String applyMapping(String input) {
Objects.requireNonNull(input);
final String output = mappingFunction.apply(input);
outputToInput.merge(
output,
input,
(previousInput, currentInput) -> {
if (previousInput.equals(currentInput)) {
return input;
}
throw new NameCollisionException(input, previousInput, output);
});
return output;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package io.github.qudtlib.constgen;

/**
* Class representing constant names/labels/local names in IRIs for generating RDF vocabularies.
*
* @author Florian Kleedorfer
* @since 1.0
*/
public class Constant {
private final String codeConstantName;
private final String iriLocalname;
private final String label;

public Constant(String codeConstantName, String iriLocalname, String label) {
this.codeConstantName = codeConstantName;
this.iriLocalname = iriLocalname;
this.label = label;
}

public String getCodeConstantName() {
return codeConstantName;
}

public String getIriLocalname() {
return iriLocalname;
}

public String getLabel() {
return label;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package io.github.qudtlib.common;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

import io.github.qudtlib.common.safenames.NameCollisionException;
import io.github.qudtlib.common.safenames.SafeStringMapper;
import org.junit.jupiter.api.Test;

public class CodeGenTest {
@Test
public void testConstantMapper() {
SafeStringMapper mapper = CodeGen.javaConstantMapper();
assertEquals("_1constant", mapper.applyMapping("1constant"));
assertEquals("_1constant", mapper.applyMapping("1constant"));
assertEquals("_2constant", mapper.applyMapping("2constant"));
assertThrows(NameCollisionException.class, () -> mapper.applyMapping("_1constant"));
}
}
2 changes: 1 addition & 1 deletion qudtlib-common/pom.xml → qudtlib-common-rdf/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>qudtlib-common</artifactId>
<artifactId>qudtlib-common-rdf</artifactId>

<dependencies>
<dependency>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.github.qudlib.common;
package io.github.qudtlib.common;

import java.io.File;
import java.io.FileOutputStream;
Expand Down Expand Up @@ -26,7 +26,7 @@
* @author Florian Kleedorfer
* @since 1.0
*/
public class RdfOps {
public abstract class RdfOps {

private static final boolean DEBUG = false;

Expand Down
9 changes: 5 additions & 4 deletions qudtlib-constants-gen/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,18 @@
<dependencies>
<dependency>
<groupId>io.github.qudtlib</groupId>
<artifactId>qudtlib-common</artifactId>
<artifactId>qudtlib-common-rdf</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.github.qudtlib</groupId>
<artifactId>qudtlib-data</artifactId>
<artifactId>qudtlib-common-codegen</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<groupId>io.github.qudtlib</groupId>
<artifactId>qudtlib-data</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>
Expand Down

This file was deleted.

Loading

0 comments on commit f9eeaea

Please sign in to comment.