use of net.morimekta.providence.model.FieldType in project providence by morimekta.
the class ProgramConverter method convert.
/**
* Convert document model to declared document.
*
* @param path The program file path.
* @param path Path of the program file to convert.
* @param program Program model to convert.
* @return The declared thrift document.
*/
public CProgram convert(String path, ProgramType program) {
ImmutableList.Builder<PDeclaredDescriptor<?>> declaredTypes = ImmutableList.builder();
ImmutableList.Builder<CConst> constants = ImmutableList.builder();
ImmutableMap.Builder<String, String> typedefs = ImmutableMap.builder();
ImmutableList.Builder<CService> services = ImmutableList.builder();
RecursiveTypeRegistry registry = programRegistry.registryForPath(path);
File dir = new File(path).getParentFile();
if (program.hasIncludes()) {
for (String include : program.getIncludes()) {
String includePath = new File(dir, include).getPath();
registry.registerInclude(ReflectionUtils.programNameFromPath(include), programRegistry.registryForPath(includePath));
}
}
for (Declaration decl : program.getDecl()) {
switch(decl.unionField()) {
case DECL_ENUM:
{
EnumType enumType = decl.getDeclEnum();
int nextValue = PEnumDescriptor.DEFAULT_FIRST_VALUE;
CEnumDescriptor type = new CEnumDescriptor(enumType.getDocumentation(), program.getProgramName(), enumType.getName(), enumType.getAnnotations());
List<CEnumValue> values = new ArrayList<>();
for (EnumValue value : enumType.getValues()) {
int v = value.hasId() ? value.getId() : nextValue;
nextValue = v + 1;
values.add(new CEnumValue(value.getDocumentation(), value.getId(), value.getName(), type, value.getAnnotations()));
}
type.setValues(values);
declaredTypes.add(type);
registry.register(type);
break;
}
case DECL_STRUCT:
{
MessageType messageType = decl.getDeclStruct();
List<CField> fields = new ArrayList<>();
if (messageType.hasFields()) {
fields.addAll(messageType.getFields().stream().map(field -> makeField(registry, program.getProgramName(), field, messageType.getVariant())).collect(Collectors.toList()));
}
PMessageDescriptor<?, ?> type;
switch(messageType.getVariant()) {
case STRUCT:
type = new CStructDescriptor(messageType.getDocumentation(), program.getProgramName(), messageType.getName(), fields, messageType.getAnnotations());
break;
case UNION:
type = new CUnionDescriptor(messageType.getDocumentation(), program.getProgramName(), messageType.getName(), fields, messageType.getAnnotations());
break;
case EXCEPTION:
type = new CExceptionDescriptor(messageType.getDocumentation(), program.getProgramName(), messageType.getName(), fields, messageType.getAnnotations());
break;
default:
throw new UnsupportedOperationException("Unhandled message variant " + messageType.getVariant());
}
declaredTypes.add(type);
registry.register(type);
break;
}
case DECL_CONST:
{
ConstType constant = decl.getDeclConst();
constants.add(makeConst(registry, program.getProgramName(), constant));
break;
}
case DECL_TYPEDEF:
{
typedefs.put(decl.getDeclTypedef().getName(), decl.getDeclTypedef().getType());
registry.registerTypedef(decl.getDeclTypedef().getName(), program.getProgramName(), decl.getDeclTypedef().getType());
break;
}
case DECL_SERVICE:
{
ServiceType serviceType = decl.getDeclService();
ImmutableList.Builder<CServiceMethod> methodBuilder = ImmutableList.builder();
if (serviceType.hasMethods()) {
for (FunctionType sm : serviceType.getMethods()) {
List<CField> rqFields = new ArrayList<>();
if (sm.numParams() > 0) {
for (FieldType field : sm.getParams()) {
rqFields.add(makeField(registry, program.getProgramName(), field, MessageVariant.STRUCT));
}
}
CStructDescriptor request = new CStructDescriptor(null, program.getProgramName(), serviceType.getName() + '.' + sm.getName() + ".request", rqFields, null);
CUnionDescriptor response = null;
if (!sm.isOneWay()) {
List<CField> rsFields = new ArrayList<>();
CField success;
if (sm.getReturnType() != null) {
PDescriptorProvider type = registry.getProvider(sm.getReturnType(), program.getProgramName(), sm.getAnnotations());
success = new CField(null, 0, PRequirement.OPTIONAL, "success", type, null, null);
} else {
success = new CField(null, 0, PRequirement.OPTIONAL, "success", PPrimitive.VOID.provider(), null, null);
}
rsFields.add(success);
if (sm.numExceptions() > 0) {
for (FieldType field : sm.getExceptions()) {
rsFields.add(makeField(registry, program.getProgramName(), field, MessageVariant.UNION));
}
}
response = new CUnionDescriptor(null, program.getProgramName(), serviceType.getName() + '.' + sm.getName() + ".response", rsFields, null);
}
CServiceMethod method = new CServiceMethod(sm.getDocumentation(), sm.getName(), sm.isOneWay(), request, response, sm.getAnnotations());
methodBuilder.add(method);
}
// for each method
}
// if has methods
PServiceProvider extendsProvider = null;
if (serviceType.hasExtend()) {
extendsProvider = registry.getServiceProvider(serviceType.getExtend(), program.getProgramName());
}
CService service = new CService(serviceType.getDocumentation(), program.getProgramName(), serviceType.getName(), extendsProvider, methodBuilder.build(), serviceType.getAnnotations());
services.add(service);
registry.registerRecursively(service);
}
}
}
return new CProgram(path, program.getDocumentation(), program.getProgramName(), program.getNamespaces(), getIncludedProgramNames(program), program.getIncludes(), typedefs.build(), declaredTypes.build(), services.build(), constants.build());
}
use of net.morimekta.providence.model.FieldType in project providence by morimekta.
the class ThriftProgramParser method parseMessage.
private MessageType parseMessage(ThriftTokenizer tokenizer, String variant, String comment, Set<String> includedPrograms) throws IOException {
MessageType._Builder struct = MessageType.builder();
if (comment != null) {
struct.setDocumentation(comment);
comment = null;
}
boolean union = variant.equals("union");
if (!variant.equals("struct")) {
struct.setVariant(MessageVariant.valueForName(variant.toUpperCase(Locale.US)));
}
Token nameToken = tokenizer.expectIdentifier("message name identifier");
String name = nameToken.asString();
if (!allowedNameIdentifier(name)) {
throw tokenizer.failure(nameToken, "Message with reserved name: " + name);
}
struct.setName(name);
int nextAutoFieldKey = -1;
tokenizer.expectSymbol("message start", Token.kMessageStart);
Set<String> fieldNames = new HashSet<>();
Set<String> fieldNameVariants = new HashSet<>();
Set<Integer> fieldIds = new HashSet<>();
while (true) {
Token token = tokenizer.expect("field def or message end");
if (token.isSymbol(Token.kMessageEnd)) {
break;
} else if (token.strEquals(kLineCommentStart)) {
comment = parseDocLine(tokenizer, comment);
continue;
} else if (token.strEquals(kBlockCommentStart)) {
comment = tokenizer.parseDocBlock();
continue;
}
FieldType._Builder field = FieldType.builder();
field.setDocumentation(comment);
comment = null;
if (token.isInteger()) {
int fId = (int) token.parseInteger();
if (fId < 1) {
throw tokenizer.failure(token, "Negative or 0 field id " + fId + " not allowed.");
}
if (fieldIds.contains(fId)) {
throw tokenizer.failure(token, "Field id " + fId + " already exists in " + struct.build().getName());
}
fieldIds.add(fId);
field.setId(fId);
tokenizer.expectSymbol("field id sep", Token.kKeyValueSep);
token = tokenizer.expect("field requirement or type", t -> t.isIdentifier() || t.isQualifiedIdentifier());
} else {
if (requireFieldId) {
throw tokenizer.failure(token, "Missing field ID in strict declaration");
}
field.setId(nextAutoFieldKey--);
}
if (token.strEquals(kRequired)) {
if (union) {
throw tokenizer.failure(token, "Found required field in union");
}
field.setRequirement(FieldRequirement.REQUIRED);
token = tokenizer.expect("field type", t -> t.isIdentifier() || t.isQualifiedIdentifier());
} else if (token.strEquals(kOptional)) {
if (!union) {
// All union fields are optional regardless.
field.setRequirement(FieldRequirement.OPTIONAL);
}
token = tokenizer.expect("field type", t -> t.isIdentifier() || t.isQualifiedIdentifier());
}
// Get type.... This is mandatory.
field.setType(parseType(tokenizer, token, includedPrograms));
nameToken = tokenizer.expectIdentifier("field name");
String fName = nameToken.asString();
if (!allowedNameIdentifier(fName)) {
throw tokenizer.failure(nameToken, "Field with reserved name: " + fName);
}
if (fieldNames.contains(fName)) {
throw tokenizer.failure(nameToken, "Field %s already exists in %s", fName, struct.build().getName());
}
if (fieldNameVariants.contains(Strings.camelCase("get", fName))) {
throw tokenizer.failure(nameToken, "Field %s has field with conflicting name in %s", fName, struct.build().getName());
}
fieldNames.add(fName);
fieldNameVariants.add(Strings.camelCase("get", fName));
field.setName(fName);
token = tokenizer.peek("default sep, annotation, field def or message end");
// Default value
if (token.isSymbol(Token.kFieldValueSep)) {
tokenizer.next();
Token defaultValue = tokenizer.parseValue();
field.setDefaultValue(defaultValue.asString());
field.setStartLineNo(defaultValue.getLineNo());
field.setStartLinePos(defaultValue.getLinePos());
token = tokenizer.peek("field annotation, def or message end");
}
// Annotation
if (token.isSymbol(Token.kParamsStart)) {
tokenizer.next();
field.setAnnotations(parseAnnotations(tokenizer, "field"));
token = tokenizer.peek("field def or message end");
}
struct.addToFields(field.build());
if (token.isSymbol(Token.kLineSep1) || token.isSymbol(Token.kLineSep2)) {
tokenizer.next();
}
}
if (tokenizer.hasNext()) {
Token token = tokenizer.peek("optional annotations");
if (token.isSymbol(Token.kParamsStart)) {
tokenizer.next();
struct.setAnnotations(parseAnnotations(tokenizer, "message"));
}
}
return struct.build();
}
use of net.morimekta.providence.model.FieldType in project providence by morimekta.
the class ThriftProgramParser method parseService.
private ServiceType parseService(ThriftTokenizer tokenizer, String doc_string, Set<String> includedPrograms) throws IOException {
ServiceType._Builder service = ServiceType.builder();
if (doc_string != null) {
service.setDocumentation(doc_string);
doc_string = null;
}
Token identifier = tokenizer.expectIdentifier("service name");
if (!allowedNameIdentifier(identifier.asString())) {
throw tokenizer.failure(identifier, "Service with reserved name: " + identifier.asString());
}
service.setName(identifier.asString());
if (tokenizer.peek("service start or extends").strEquals(kExtends)) {
tokenizer.next();
service.setExtend(tokenizer.expect("service extending identifier", t -> t.isIdentifier() || t.isQualifiedIdentifier()).asString());
}
tokenizer.expectSymbol("reading service start", Token.kMessageStart);
Set<String> methodNames = new TreeSet<>();
while (true) {
Token token = tokenizer.expect("service method initializer");
if (token.isSymbol(Token.kMessageEnd)) {
break;
} else if (token.strEquals(kLineCommentStart)) {
doc_string = parseDocLine(tokenizer, doc_string);
continue;
} else if (token.strEquals(kBlockCommentStart)) {
doc_string = tokenizer.parseDocBlock();
continue;
}
FunctionType._Builder method = FunctionType.builder();
if (doc_string != null) {
method.setDocumentation(doc_string);
doc_string = null;
}
if (token.strEquals(kOneway)) {
method.setOneWay(true);
token = tokenizer.expect("service method type");
}
if (!token.strEquals(kVoid)) {
if (method.isSetOneWay()) {
throw tokenizer.failure(token, "Oneway methods must have void return type, found '%s'", Strings.escape(token.asString()));
}
method.setReturnType(parseType(tokenizer, token, includedPrograms));
}
token = tokenizer.expectIdentifier("method name");
String name = token.asString();
if (!allowedNameIdentifier(name)) {
throw tokenizer.failure(token, "Method with reserved name: " + name);
}
String normalized = Strings.camelCase("", name);
if (methodNames.contains(normalized)) {
throw tokenizer.failure(token, "Service method " + name + " has normalized name conflict");
}
methodNames.add(normalized);
method.setName(name);
tokenizer.expectSymbol("method params begin", Token.kParamsStart);
int nextAutoParamKey = -1;
while (true) {
token = tokenizer.expect("method params");
if (token.isSymbol(Token.kParamsEnd)) {
break;
} else if (token.strEquals(kLineCommentStart)) {
doc_string = parseDocLine(tokenizer, doc_string);
continue;
} else if (token.strEquals(kBlockCommentStart)) {
doc_string = tokenizer.parseDocBlock();
continue;
}
FieldType._Builder field = FieldType.builder();
if (doc_string != null) {
field.setDocumentation(doc_string);
doc_string = null;
}
if (token.isInteger()) {
field.setId((int) token.parseInteger());
tokenizer.expectSymbol("params kv sep", Token.kKeyValueSep);
token = tokenizer.expect("param type");
} else {
if (requireFieldId) {
throw tokenizer.failure(token, "Missing param ID in strict declaration");
}
field.setId(nextAutoParamKey--);
}
if (PRequirement.OPTIONAL.label.equals(token.asString())) {
field.setRequirement(FieldRequirement.OPTIONAL);
token = tokenizer.expect("param type");
} else if (PRequirement.REQUIRED.label.equals(token.asString())) {
field.setRequirement(FieldRequirement.REQUIRED);
token = tokenizer.expect("param type");
}
field.setType(parseType(tokenizer, token, includedPrograms));
token = tokenizer.expectIdentifier("param name");
name = token.asString();
if (!allowedNameIdentifier(name)) {
throw tokenizer.failure(token, "Param with reserved name: " + name);
}
field.setName(name);
// Annotations.
if (tokenizer.peek("method param annotation").isSymbol(Token.kParamsStart)) {
tokenizer.next();
field.setAnnotations(parseAnnotations(tokenizer, "params"));
}
token = tokenizer.peek("method params");
if (token.isSymbol(Token.kLineSep1) || token.isSymbol(Token.kLineSep2)) {
tokenizer.next();
}
method.addToParams(field.build());
}
// for each param
doc_string = null;
if (tokenizer.peek("possible throws statement").strEquals(kThrows)) {
tokenizer.next();
tokenizer.expectSymbol("throws group start", Token.kParamsStart);
int nextAutoExceptionKey = -1;
while (true) {
token = tokenizer.expect("exception key, type or end throws");
if (token.isSymbol(Token.kParamsEnd)) {
break;
} else if (token.strEquals(kLineCommentStart)) {
doc_string = parseDocLine(tokenizer, doc_string);
continue;
} else if (token.strEquals(kBlockCommentStart)) {
doc_string = tokenizer.parseDocBlock();
continue;
}
FieldType._Builder field = FieldType.builder();
if (doc_string != null) {
field.setDocumentation(doc_string);
doc_string = null;
}
if (token.isInteger()) {
field.setId((int) token.parseInteger());
tokenizer.expectSymbol("exception KV sep", Token.kKeyValueSep);
token = tokenizer.expect("exception type");
} else {
if (requireFieldId) {
throw tokenizer.failure(token, "Missing exception ID in strict declaration");
}
field.setId(nextAutoExceptionKey--);
}
field.setType(parseType(tokenizer, token, includedPrograms));
token = tokenizer.expectIdentifier("exception name");
name = token.asString();
if (!allowedNameIdentifier(name)) {
throw tokenizer.failure(token, "Thrown field with reserved name: " + name);
}
field.setName(name);
// Annotations.
if (tokenizer.peek("exception annotation start").isSymbol(Token.kParamsStart)) {
tokenizer.next();
field.setAnnotations(parseAnnotations(tokenizer, "exception"));
}
method.addToExceptions(field.build());
token = tokenizer.peek("method exceptions");
if (token.isSymbol(Token.kLineSep1) || token.isSymbol(Token.kLineSep2)) {
tokenizer.next();
}
}
}
token = tokenizer.peek("");
// Method Annotations.
if (token.isSymbol(Token.kParamsStart)) {
tokenizer.next();
method.setAnnotations(parseAnnotations(tokenizer, "method"));
token = tokenizer.peek("method or service end");
}
service.addToMethods(method.build());
if (token.isSymbol(Token.kLineSep1) || token.isSymbol(Token.kLineSep2)) {
tokenizer.next();
}
}
if (tokenizer.hasNext()) {
Token token = tokenizer.peek("optional annotations");
if (token.isSymbol(Token.kParamsStart)) {
// Method Annotations.
tokenizer.next();
service.setAnnotations(parseAnnotations(tokenizer, "service"));
}
}
return service.build();
}
Aggregations