blob: 6798629e835c727770315d93c9789add8a3debee [file] [log] [blame]
package org.jetbrains.protocolReader;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jsonProtocol.ItemDescriptor;
import org.jetbrains.jsonProtocol.ProtocolMetaModel;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
class DomainGenerator {
final ProtocolMetaModel.Domain domain;
final Generator generator;
DomainGenerator(Generator generator, ProtocolMetaModel.Domain domain) {
this.generator = generator;
this.domain = domain;
}
void registerTypes() {
if (domain.types() != null) {
for (ProtocolMetaModel.StandaloneType type : domain.types()) {
generator.typeMap.getTypeData(domain.domain(), type.id()).setType(type);
}
}
}
void generateCommandsAndEvents() throws IOException {
for (ProtocolMetaModel.Command command : domain.commands()) {
boolean hasResponse = command.returns() != null;
generateCommandParams(command, hasResponse);
if (hasResponse) {
String className = generator.getNaming().commandResult.getShortName(command.name());
FileUpdater fileUpdater = generator.startJavaFile(generator.getNaming().commandResult, domain, command.name());
generateJsonProtocolInterface(fileUpdater.out, className, command.description(), command.returns(), null);
fileUpdater.update();
String dataFullName = generator.getNaming().commandResult.getFullName(domain.domain(), command.name()).getFullText();
generator.jsonProtocolParserClassNames.add(dataFullName);
generator.parserRootInterfaceItems.add(new ParserRootInterfaceItem(domain.domain(), command.name(), generator.getNaming().commandResult));
}
}
if (domain.events() != null) {
for (ProtocolMetaModel.Event event : domain.events()) {
generateEvenData(event);
generator.jsonProtocolParserClassNames.add(generator.getNaming().eventData.getFullName(domain.domain(), event.name()).getFullText());
generator.parserRootInterfaceItems.add(new ParserRootInterfaceItem(domain.domain(), event.name(), generator.getNaming().eventData));
}
}
}
private void generateCommandParams(final ProtocolMetaModel.Command command, final boolean hasResponse) throws IOException {
TextOutConsumer baseTypeBuilder = new TextOutConsumer() {
@Override
public void append(TextOutput out) {
out.space().append("extends ").append(generator.getNaming().requestClassName);
if (hasResponse) {
out.space().append("implements org.jetbrains.jsonProtocol.RequestWithResponse");
}
}
};
TextOutConsumer memberBuilder = new TextOutConsumer() {
@Override
public void append(TextOutput out) {
out.newLine().append("@Override").newLine().append("public String getMethodName()").openBlock();
out.append("return \"");
if (!domain.domain().isEmpty()) {
out.append(domain.domain()).append('.');
}
out.append(command.name()).append("\";").closeBlock();
}
};
generateTopLevelOutputClass(generator.getNaming().params, command.name(), command.description(), baseTypeBuilder,
memberBuilder, command.parameters());
}
void generateCommandAdditionalParam(ProtocolMetaModel.StandaloneType type) throws IOException {
generateTopLevelOutputClass(generator.getNaming().additionalParam, type.id(), type.description(), null, null, type.properties());
}
private <P extends ItemDescriptor.Named> void generateTopLevelOutputClass(ClassNameScheme nameScheme,
String baseName,
String description,
TextOutConsumer baseType,
TextOutConsumer additionalMemberText,
List<P> properties) throws IOException {
FileUpdater fileUpdater = generator.startJavaFile(nameScheme, domain, baseName);
TextOutput out = fileUpdater.out;
NamePath classNamePath = nameScheme.getFullName(domain.domain(), baseName);
generateOutputClass(out, classNamePath, description, baseType, additionalMemberText, properties);
fileUpdater.update();
}
private <P extends ItemDescriptor.Named> void generateOutputClass(TextOutput out,
NamePath classNamePath,
String description,
@Nullable TextOutConsumer baseType,
TextOutConsumer additionalMemberText,
List<P> properties) {
out.doc(description);
out.append("public final class ").append(classNamePath.getLastComponent());
if (baseType == null) {
out.append(" extends ").append("org.jetbrains.jsonProtocol.OutMessage");
}
else {
baseType.append(out);
}
OutputClassScope classScope = new OutputClassScope(this, classNamePath);
if (additionalMemberText != null) {
classScope.addMember(additionalMemberText);
}
out.openBlock();
classScope.generate(out, properties);
classScope.writeAdditionalMembers(out);
out.closeBlock();
}
StandaloneTypeBinding createStandaloneOutputTypeBinding(@NotNull ProtocolMetaModel.StandaloneType type, @NotNull String name) {
return Generator.switchByType(type, new MyCreateStandaloneTypeBindingVisitorBase(this, type, name));
}
StandaloneTypeBinding createStandaloneInputTypeBinding(ProtocolMetaModel.StandaloneType type) {
return Generator.switchByType(type, new CreateStandaloneTypeBindingVisitorBase(this, type) {
@Override
public StandaloneTypeBinding visitObject(List<ProtocolMetaModel.ObjectProperty> properties) {
return createStandaloneObjectInputTypeBinding(getType(), properties);
}
@Override
public StandaloneTypeBinding visitEnum(List<String> enumConstants) {
return createStandaloneEnumInputTypeBinding(getType(), enumConstants,
TypeData.Direction.INPUT);
}
@Override
public StandaloneTypeBinding visitArray(ProtocolMetaModel.ArrayItemType items) {
ResolveAndGenerateScope resolveAndGenerateScope = new ResolveAndGenerateScope() {
// This class is responsible for generating ad hoc type.
// If we ever are to do it, we should generate into string buffer and put strings
// inside TypeDef class.
@Override
public String getDomainName() {
return domain.domain();
}
@Override
public TypeData.Direction getTypeDirection() {
return TypeData.Direction.INPUT;
}
@Override
public <T extends ItemDescriptor> TypeDescriptor resolveType(T typedObject) {
throw new UnsupportedOperationException();
}
@Override
public BoxableType generateNestedObject(String description, List<ProtocolMetaModel.ObjectProperty> properties) {
throw new UnsupportedOperationException();
}
};
BoxableType itemBoxableType = generator.resolveType(items, resolveAndGenerateScope).getType();
final BoxableType arrayType = new ListType(itemBoxableType);
StandaloneTypeBinding.Target target = new StandaloneTypeBinding.Target() {
@Override
public BoxableType resolve(ResolveContext context) {
return arrayType;
}
};
return createTypedefTypeBinding(getType(), target, generator.getNaming().inputTypedef, TypeData.Direction.INPUT);
}
});
}
StandaloneTypeBinding createStandaloneObjectInputTypeBinding(@NotNull final ProtocolMetaModel.StandaloneType type, @Nullable final List<ProtocolMetaModel.ObjectProperty> properties) {
final String name = type.id();
final NamePath fullTypeName = generator.getNaming().inputValue.getFullName(domain.domain(), name);
generator.jsonProtocolParserClassNames.add(fullTypeName.getFullText());
return new StandaloneTypeBinding() {
@Override
public BoxableType getJavaType() {
return new StandaloneType(fullTypeName, "writeMessage");
}
@Override
public void generate() throws IOException {
NamePath className = generator.getNaming().inputValue.getFullName(domain.domain(), name);
FileUpdater fileUpdater = generator.startJavaFile(generator.getNaming().inputValue, domain, name);
TextOutput out = fileUpdater.out;
if (type.description() != null) {
out.doc(type.description());
}
out.append("@org.jetbrains.jsonProtocol.JsonType").newLine();
out.append("public interface ").append(className.getLastComponent()).openBlock();
InputClassScope classScope = new InputClassScope(DomainGenerator.this, className);
if (properties != null) {
classScope.generateDeclarationBody(out, properties);
}
classScope.writeAdditionalMembers(out);
out.closeBlock();
fileUpdater.update();
}
@Override public TypeData.Direction getDirection() {
return TypeData.Direction.INPUT;
}
};
}
StandaloneTypeBinding createStandaloneEnumInputTypeBinding(final ProtocolMetaModel.StandaloneType type,
final List<String> enumConstants, final TypeData.Direction direction) {
final String name = type.id();
return new StandaloneTypeBinding() {
@Override
public BoxableType getJavaType() {
return new StandaloneType(generator.getNaming().inputEnum.getFullName(domain.domain(), name), "writeEnum");
}
@Override
public void generate() throws IOException {
FileUpdater fileUpdater = generator.startJavaFile(generator.getNaming().inputEnum, domain, name);
fileUpdater.out.doc(type.description());
Enums.appendEnums(enumConstants, generator.getNaming().inputEnum.getShortName(name), true, fileUpdater.out);
fileUpdater.update();
}
@Override
public TypeData.Direction getDirection() {
return direction;
}
};
}
/**
* Typedef is an empty class that just holds description and
* refers to an actual type (such as String).
*/
StandaloneTypeBinding createTypedefTypeBinding(final ProtocolMetaModel.StandaloneType type, StandaloneTypeBinding.Target target,
final ClassNameScheme nameScheme, final TypeData.Direction direction) {
final String name = type.id();
final NamePath typedefJavaName = nameScheme.getFullName(domain.domain(), name);
final List<TextOutput> deferredWriters = new ArrayList<>();
final BoxableType actualJavaType = target.resolve(new StandaloneTypeBinding.Target.ResolveContext() {
@Override
public BoxableType generateNestedObject(String shortName, String description, List<ProtocolMetaModel.ObjectProperty> properties) {
NamePath classNamePath = new NamePath(shortName, typedefJavaName);
if (direction == null) {
throw new RuntimeException("Unsupported");
}
switch (direction) {
case INPUT:
throw new RuntimeException("TODO");
case OUTPUT:
TextOutput out = new TextOutput(new StringBuilder());
generateOutputClass(out, classNamePath, description, null, null, properties);
deferredWriters.add(out);
break;
default:
throw new RuntimeException();
}
return new StandaloneType(new NamePath(shortName, typedefJavaName), "writeMessage");
}
});
return new StandaloneTypeBinding() {
@Override
public BoxableType getJavaType() {
return actualJavaType;
}
@Override
public void generate() {
}
@Override
public TypeData.Direction getDirection() {
return direction;
}
};
}
private void generateEvenData(final ProtocolMetaModel.Event event) throws IOException {
String className = generator.getNaming().eventData.getShortName(event.name());
FileUpdater fileUpdater = generator.startJavaFile(generator.getNaming().eventData, domain, event.name());
final String domainName = domain.domain();
final CharSequence fullName = generator.getNaming().eventData.getFullName(domainName, event.name()).getFullText();
generateJsonProtocolInterface(fileUpdater.out, className, event.description(), event.parameters(), new TextOutConsumer() {
@Override
public void append(TextOutput out) {
out.newLine().append("org.jetbrains.wip.protocol.WipEventType<").append(fullName).append("> TYPE").newLine();
out.append("\t= new org.jetbrains.wip.protocol.WipEventType<").append(fullName).append(">");
out.append("(\"").append(domainName).append('.').append(event.name()).append("\", ").append(fullName).append(".class)").openBlock();
{
out.append("@Override").newLine().append("public ").append(fullName).append(" read(");
out.append(generator.getNaming().inputPackage).append('.').append(Generator.READER_INTERFACE_NAME + " protocolReader, ").append(Util.JSON_READER_PARAMETER_DEF).append(")").openBlock();
out.append("return protocolReader.").append(generator.getNaming().eventData.getParseMethodName(domainName, event.name())).append("(reader);").closeBlock();
}
out.closeBlock();
out.semi();
}
});
fileUpdater.update();
}
private void generateJsonProtocolInterface(TextOutput out, String className, String description, List<ProtocolMetaModel.Parameter> parameters, TextOutConsumer additionalMembersText) {
if (description != null) {
out.doc(description);
}
out.append("@org.jetbrains.jsonProtocol.JsonType").newLine().append("public interface ").append(className).openBlock();
InputClassScope classScope = new InputClassScope(this, new NamePath(className, new NamePath(ClassNameScheme.getPackageName(generator.getNaming().inputPackage, domain.domain()))));
if (additionalMembersText != null) {
classScope.addMember(additionalMembersText);
}
if (parameters != null) {
classScope.generateDeclarationBody(out, parameters);
}
classScope.writeAdditionalMembers(out);
out.closeBlock();
}
}