use of net.morimekta.providence.model.ProgramType in project providence by morimekta.
the class TypeLoader method loadInternal.
private ProgramTypeRegistry loadInternal(File file, List<String> loadStack) throws IOException {
loadStack = new ArrayList<>(loadStack);
file = file.getCanonicalFile();
if (!file.exists()) {
throw new IllegalArgumentException("No such file " + file);
}
if (!file.isFile()) {
throw new IllegalArgumentException("Unable to load thrift program: " + file + " is not a file.");
}
file = file.getAbsoluteFile();
String path = file.getPath();
if (loadStack.contains(path)) {
// Only show the circular includes, not the path to get there.
while (!loadStack.get(0).equals(path)) {
loadStack.remove(0);
}
loadStack.add(path);
String prefix = longestCommonPrefixPath(loadStack);
if (prefix.length() > 0) {
loadStack = stripCommonPrefix(loadStack);
throw new IllegalArgumentException("Circular includes detected: " + prefix + "... " + String.join(" -> ", loadStack));
}
throw new IllegalArgumentException("Circular includes detected: " + String.join(" -> ", loadStack));
}
loadStack.add(path);
ProgramTypeRegistry registry = this.programRegistry.registryForPath(path);
if (programRegistry.containsProgramPath(path)) {
return registry;
}
InputStream in = new BufferedInputStream(new FileInputStream(file));
ProgramType doc = parser.parse(in, file, includes);
ArrayList<File> queue = new ArrayList<>();
if (doc.hasIncludes()) {
for (String include : doc.getIncludes()) {
File location = new File(file.getParent(), include).getCanonicalFile();
if (!location.exists()) {
if (include.startsWith(".") || include.startsWith(File.separator)) {
throw new ParseException("No such file \"" + include + "\" to include from " + file.getName());
}
for (File inc : includes) {
File i = new File(inc, include);
if (i.exists()) {
location = i.getCanonicalFile();
break;
}
}
}
if (location.exists() && !queue.contains(location)) {
queue.add(location.getAbsoluteFile());
}
}
}
// Load includes in reverse order, in case of serial dependencies.
Collections.reverse(queue);
loadedDocuments.put(path, doc);
for (File include : queue) {
registry.registerInclude(programNameFromPath(include.getPath()), loadInternal(include, ImmutableList.copyOf(loadStack)));
}
// Now everything it depends on is loaded.
CProgram program = converter.convert(path, doc);
programRegistry.putProgram(path, program);
programRegistry.putProgramType(path, doc);
return registry;
}
use of net.morimekta.providence.model.ProgramType in project providence by morimekta.
the class JsonGenerator method generate.
@Override
public void generate(ProgramTypeRegistry registry) throws IOException, GeneratorException {
ProgramType doc = registry.getProgramType();
if (doc.hasIncludes()) {
doc = doc.mutate().setIncludes(doc.getIncludes().stream().map(path -> path.replaceAll("(\\.thrift)$", ".json")).collect(Collectors.toList())).build();
}
OutputStream out = getFileManager().create(null, doc.getProgramName() + ".json");
try {
serializer.serialize(out, doc);
out.write('\n');
} catch (SerializerException e) {
throw new GeneratorException("Unable to serialize document.", e);
}
getFileManager().finalize(out);
}
use of net.morimekta.providence.model.ProgramType in project providence by morimekta.
the class MessageProgramParserTest method testParse.
@Test
public void testParse() throws IOException {
MessageProgramParser parser = new MessageProgramParser(new JsonSerializer());
ByteArrayInputStream in = new ByteArrayInputStream(("{\n" + " \"namespaces\": {\n" + " \"java\": \"net.morimekta.providence\"\n" + " }\n" + "}").getBytes(StandardCharsets.UTF_8));
ProgramType program = parser.parse(in, new File("test.json"), ImmutableSet.of());
assertThat(program.getNamespaces(), is(ImmutableMap.of("java", "net.morimekta.providence")));
}
use of net.morimekta.providence.model.ProgramType in project providence by morimekta.
the class ThriftProgramParserTest method testAutoValue.
@Test
public void testAutoValue() throws IOException {
copyResourceTo("/parser/tests/autovalue.thrift", tmp.getRoot());
File autovalue = new File(tmp.getRoot(), "autovalue.thrift");
ThriftProgramParser parser = new ThriftProgramParser();
ProgramType program = parser.parse(new FileInputStream(autovalue), autovalue, new TreeSet<>());
assertEquals("{\n" + " program_name = \"autovalue\"\n" + " namespaces = {\n" + " \"java\": \"net.morimekta.test.autoid\"\n" + " }\n" + " decl = [\n" + " {\n" + " decl_enum = {\n" + " name = \"AutoValue\"\n" + " values = [\n" + " {\n" + " name = \"FIRST\"\n" + " id = 0\n" + " },\n" + " {\n" + " name = \"SECOND\"\n" + " id = 1\n" + " },\n" + " {\n" + " name = \"THIRD\"\n" + " id = 2\n" + " }\n" + " ]\n" + " }\n" + " },\n" + " {\n" + " decl_enum = {\n" + " name = \"PartialAutoValue\"\n" + " values = [\n" + " {\n" + " name = \"FIRST\"\n" + " id = 5\n" + " },\n" + " {\n" + " name = \"SECOND\"\n" + " id = 6\n" + " },\n" + " {\n" + " name = \"THIRD\"\n" + " id = 7\n" + " }\n" + " ]\n" + " }\n" + " }\n" + " ]\n" + "}", debugString(program));
}
use of net.morimekta.providence.model.ProgramType in project providence by morimekta.
the class ThriftProgramParserTest method testParse_calculator_strict.
@Test
public void testParse_calculator_strict() throws IOException {
copyResourceTo("/parser/calculator/number.thrift", tmp.getRoot());
copyResourceTo("/parser/calculator/calculator_strict.thrift", tmp.getRoot());
File calculator = new File(tmp.getRoot(), "calculator_strict.thrift");
ThriftProgramParser parser = new ThriftProgramParser(true, true);
ProgramType program = parser.parse(new FileInputStream(calculator), calculator, new TreeSet<>());
assertThat(debugString(program), is(equalToLines("{\n" + " program_name = \"calculator_strict\"\n" + " includes = [\n" + " \"number.thrift\"\n" + " ]\n" + " namespaces = {\n" + " \"java\": \"net.morimekta.test.calculator\"\n" + " }\n" + " decl = [\n" + " {\n" + " decl_enum = {\n" + " documentation = \"Block comment on type.\"\n" + " name = \"Operator\"\n" + " values = [\n" + " {\n" + " documentation = \"line comment on enum\"\n" + " name = \"IDENTITY\"\n" + " id = 1\n" + " },\n" + " {\n" + " documentation = \"Block comment on enum.\"\n" + " name = \"ADD\"\n" + " id = 2\n" + " },\n" + " {\n" + " name = \"SUBTRACT\"\n" + " id = 3\n" + " },\n" + " {\n" + " name = \"MULTIPLY\"\n" + " id = 4\n" + " },\n" + " {\n" + " name = \"DIVIDE\"\n" + " id = 5\n" + " }\n" + " ]\n" + " }\n" + " },\n" + " {\n" + " decl_struct = {\n" + " documentation = \"Line comment on type.\"\n" + " variant = UNION\n" + " name = \"Operand\"\n" + " fields = [\n" + " {\n" + " documentation = \"Double line\\ncomment on field.\"\n" + " id = 1\n" + " type = \"Operation\"\n" + " name = \"operation\"\n" + " },\n" + " {\n" + " documentation = \"Block comment\\n - with formatting.\\nOn field.\"\n" + " id = 2\n" + " type = \"double\"\n" + " name = \"number\"\n" + " },\n" + " {\n" + " id = 3\n" + " type = \"number.Imaginary\"\n" + " name = \"imaginary\"\n" + " }\n" + " ]\n" + " }\n" + " },\n" + " {\n" + " decl_struct = {\n" + " name = \"Operation\"\n" + " fields = [\n" + " {\n" + " id = 1\n" + " type = \"Operator\"\n" + " name = \"operator\"\n" + " },\n" + " {\n" + " id = 2\n" + " type = \"list<Operand>\"\n" + " name = \"operands\"\n" + " }\n" + " ]\n" + " annotations = {\n" + " \"compact\": \"\"\n" + " }\n" + " }\n" + " },\n" + " {\n" + " decl_struct = {\n" + " variant = EXCEPTION\n" + " name = \"CalculateException\"\n" + " fields = [\n" + " {\n" + " id = 1\n" + " requirement = REQUIRED\n" + " type = \"string\"\n" + " name = \"message\"\n" + " },\n" + " {\n" + " id = 2\n" + " type = \"Operation\"\n" + " name = \"operation\"\n" + " }\n" + " ]\n" + " }\n" + " },\n" + " {\n" + " decl_service = {\n" + " name = \"Calculator\"\n" + " methods = [\n" + " {\n" + " documentation = \"Block comment on method.\"\n" + " return_type = \"Operand\"\n" + " name = \"calculate\"\n" + " params = [\n" + " {\n" + " id = 1\n" + " type = \"Operation\"\n" + " name = \"op\"\n" + " }\n" + " ]\n" + " exceptions = [\n" + " {\n" + " id = 1\n" + " type = \"CalculateException\"\n" + " name = \"ce\"\n" + " }\n" + " ]\n" + " },\n" + " {\n" + " documentation = \"line comment on method.\"\n" + " one_way = true\n" + " name = \"iamalive\"\n" + " params = []\n" + " }\n" + " ]\n" + " }\n" + " },\n" + " {\n" + " decl_const = {\n" + " documentation = \"Block comment on constant.\"\n" + " type = \"Operand\"\n" + " name = \"PI\"\n" + " value = \"{\\n \\\"number\\\": 3.141592\\n}\"\n" + " start_line_no = 56\n" + " start_line_pos = 20\n" + " }\n" + " },\n" + " {\n" + " decl_const = {\n" + " documentation = \"Line comment on constant.\"\n" + " type = \"set<Operator>\"\n" + " name = \"kComplexOperands\"\n" + " value = \"[\\n Operator.MULTIPLY,\\n Operator.DIVIDE\\n]\"\n" + " start_line_no = 61\n" + " start_line_pos = 40\n" + " }\n" + " }\n" + " ]\n" + "}")));
}
Aggregations