Commit 3b846170 authored by Martti Käärik's avatar Martti Käärik
Browse files

Generate Java mappings for OpenAPI-based types using Java code generator...

Generate Java mappings for OpenAPI-based types using Java code generator processing (so we get exact class/field/method names as generated code).
parent 29b15fe6
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -191,6 +191,7 @@ public class TDLHelper {

	private static final String TDL_MODEL_NAME = "tdl";
	private static final String HTTP_MODEL_NAME = "http";
	private static final String JAVA_MODEL_NAME = "java";
	private static final String[] MODEL_FILE_EXTENSIONS = new String[] {
			"tdl", "tdlan2", "tdltx", "tdltxi"
	};
@@ -239,6 +240,10 @@ public class TDLHelper {
		return getKnownPackage(resource, HTTP_MODEL_NAME);
	}
	
	public static Package getJavaPackage(Resource resource) {
		return getKnownPackage(resource, JAVA_MODEL_NAME);
	}
	
	private static Package getKnownPackage(Resource resource, String packageName) {
		try {
			IProject prj = getProjectForResource(resource);
+169 −34
Original line number Diff line number Diff line
@@ -10,20 +10,32 @@ import java.util.Set;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ProjectScope;
import org.eclipse.emf.ecore.resource.Resource;
import org.etsi.mts.tdl.Annotation;
import org.etsi.mts.tdl.AnnotationType;
import org.etsi.mts.tdl.CollectionDataType;
import org.etsi.mts.tdl.DataElementMapping;
import org.etsi.mts.tdl.DataResourceMapping;
import org.etsi.mts.tdl.DataType;
import org.etsi.mts.tdl.ElementImport;
import org.etsi.mts.tdl.Extension;
import org.etsi.mts.tdl.Member;
import org.etsi.mts.tdl.Package;
import org.etsi.mts.tdl.PackageableElement;
import org.etsi.mts.tdl.ParameterMapping;
import org.etsi.mts.tdl.SimpleDataType;
import org.etsi.mts.tdl.StructuredDataType;
import org.etsi.mts.tdl.tdlFactory;
import org.etsi.mts.tdl.tdlPackage;
import org.etsi.mts.tdl.helper.TDLHelper;
import org.etsi.mts.tdl.transform.AbstractTranslator;
import org.openapitools.codegen.ClientOptInput;
import org.openapitools.codegen.CodegenConfig;
import org.openapitools.codegen.CodegenModel;
import org.openapitools.codegen.CodegenProperty;
import org.openapitools.codegen.DefaultGenerator;
import org.openapitools.codegen.config.CodegenConfigurator;
import org.openapitools.codegen.languages.JavaClientCodegen;
import org.openapitools.codegen.model.ModelMap;
import org.openapitools.codegen.utils.ModelUtils;
import org.osgi.service.prefs.Preferences;

@@ -52,7 +64,11 @@ public class OpenAPI2TDLTranslatorNext extends AbstractTranslator {
	public void translate(String filename, String sourceMappingTag, String targetMappingTag) throws Exception {
		translate(filename, sourceMappingTag, targetMappingTag, false);
	}
	public void translate(String filename, String sourceMappingTag, String targetMappingTag, boolean inline) throws Exception {

	public void translate(String filename, String sourceMappingTag, String targetMappingTag, boolean inline)
			throws Exception {
		sourceMappingTag = "OpenAPISpecification";
		targetMappingTag = "JavaTypesPackage";
//		Converter.setTdlTokens(Arrays.asList(new String[] {"'name'", "'type'", "'size'", "'instance'"}));
//		Converter converter = new Converter(filename);
//		converter.runConversion();
@@ -65,9 +81,6 @@ public class OpenAPI2TDLTranslatorNext extends AbstractTranslator {
			getGeneratedPackage().setName(cleanName(model.getInfo().getTitle()));
		drm = getTypeFor(sourceMappingTag, tdlPackage.Literals.DATA_RESOURCE_MAPPING);
		drm.setResourceURI(new File(filename).getName());
		drmTarget = getTypeFor(targetMappingTag, tdlPackage.Literals.DATA_RESOURCE_MAPPING);
		// TODO: make configurable
		drmTarget.setResourceURI("generated/java");


		theTdlPackage = TDLHelper.getTdlPackage(getTargetResource());
@@ -96,7 +109,8 @@ public class OpenAPI2TDLTranslatorNext extends AbstractTranslator {
				Package messageBased = null;
				packages: for (Package p : httpPackage.getNestedPackage()) {
					for (PackageableElement e : p.getPackagedElement()) {
						if (e.eClass().equals(tdlPackage.eINSTANCE.getStructuredDataType()) && e.getName().equals("Body")) {
						if (e.eClass().equals(tdlPackage.eINSTANCE.getStructuredDataType())
								&& e.getName().equals("Body")) {
							messageBody = (StructuredDataType) e;
							messageBased = p;
							break packages;
@@ -111,13 +125,48 @@ public class OpenAPI2TDLTranslatorNext extends AbstractTranslator {
			}
		}

		// Java mappings
		Package mappingsPackage = tdlFactory.eINSTANCE.createPackage();
		mappingsPackage.setName("JavaMapping");
		getGeneratedPackage().getNestedPackage().add(mappingsPackage);
		ElementImport typesImport = tdlFactory.eINSTANCE.createElementImport();
		typesImport.setImportedPackage(getGeneratedPackage());
		mappingsPackage.getImport().add(typesImport);
		if (theTdlPackage != null) {
			ElementImport tdlImport = tdlFactory.eINSTANCE.createElementImport();
			tdlImport.setImportedPackage(theTdlPackage);
			mappingsPackage.getImport().add(tdlImport);
		}
		Package javaPackage = TDLHelper.getJavaPackage(getTargetResource());
		if (javaPackage != null) {
			ElementImport javaImport = tdlFactory.eINSTANCE.createElementImport();
			javaImport.setImportedPackage(javaPackage);
			mappingsPackage.getImport().add(javaImport);
		}
		drmTarget = getTypeFor(targetMappingTag, tdlPackage.Literals.DATA_RESOURCE_MAPPING);
		mappingsPackage.getPackagedElement().add(drmTarget);
		drmTarget.setResourceURI(preferences.get(PropertyPage.JAVA_API_PACKAGE, "generated.java"));
		if (javaPackage != null) {
			for (PackageableElement e : javaPackage.getPackagedElement()) {
				if (e.eClass().equals(tdlPackage.eINSTANCE.getAnnotationType())
						&& e.getName().equals("JavaPackage")) {
					Annotation annot = tdlFactory.eINSTANCE.createAnnotation();
					annot.setKey((AnnotationType) e);
					drmTarget.getAnnotation().add(annot);
					break;
				}
			}
		}

		for (String schemaName : model.getComponents().getSchemas().keySet()) {
			Schema<?> schema = model.getComponents().getSchemas().get(schemaName);
			schema.setName(schemaName);
			DataType dataType = translate(schema, "", messageBody);
			addMapping(schema, dataType, sourceMappingTag, targetMappingTag);
			//addMapping(schema, dataType, sourceMappingTag, targetMappingTag);
		}
		
		addMappings(filename, mappingsPackage, drmTarget, javaPackage);

	}

	@Override
@@ -139,10 +188,12 @@ public class OpenAPI2TDLTranslatorNext extends AbstractTranslator {
	}

	private void addMapping(Schema<?> schema, DataType dataType, String sourceMappingTag, String targetMappingTag) {
		DataElementMapping sourceMapping = addDataElementMapping("#/components/schemas/"+schema.getName()+"", dataType, sourceMappingTag);
		DataElementMapping sourceMapping = addDataElementMapping("#/components/schemas/" + schema.getName() + "",
				dataType, sourceMappingTag);

		// TODO: make configurable?
		DataElementMapping targetMapping = addDataElementMapping(""+schema.getName()+"", dataType, targetMappingTag);
		DataElementMapping targetMapping = addDataElementMapping("" + schema.getName() + "", dataType,
				targetMappingTag);
		targetMapping.setDataResourceMapping(drmTarget);

		if (dataType instanceof StructuredDataType) {
@@ -164,8 +215,8 @@ public class OpenAPI2TDLTranslatorNext extends AbstractTranslator {
			if (schema.getName() == null) {
				name = prefix + "___item";
			}
			if (schema.getProperties() !=null && !schema.getProperties().isEmpty() || 
					schema.getType().equals("object")) {
			if (schema.getProperties() != null && !schema.getProperties().isEmpty()
					|| schema.getType().equals("object")) {
				DataType t = translateObject(schema, name);
				addSuperType(t, superType);
				return t;
@@ -183,10 +234,9 @@ public class OpenAPI2TDLTranslatorNext extends AbstractTranslator {
			name = schema.getType();

		if (this.theTdlPackage != null) {
			if (name.equals("string") || 
					name.equals("integer") || 
					name.equals("boolean")) {
				name = name.replaceFirst(Character.toString(name.charAt(0)), Character.toString(name.charAt(0)).toUpperCase());
			if (name.equals("string") || name.equals("integer") || name.equals("boolean")) {
				name = name.replaceFirst(Character.toString(name.charAt(0)),
						Character.toString(name.charAt(0)).toUpperCase());
				for (PackageableElement pe : this.theTdlPackage.getPackagedElement()) {
					if (pe.eClass().equals(tdlPackage.eINSTANCE.getSimpleDataType()) && pe.getName().equals(name))
						return (DataType) pe;
@@ -273,9 +323,7 @@ public class OpenAPI2TDLTranslatorNext extends AbstractTranslator {
			replacement = replacement.replaceAll(idInvalidCharRegex, "_");
			// This isn't needed because keywords are escaped by IDValueConverter
			/*
			if (xtextKyewords.contains(replacement)) {
				replacement = "^" + replacement;
			}
			 * if (xtextKyewords.contains(replacement)) { replacement = "^" + replacement; }
			 */
		}
		// TODO check for duplicates
@@ -284,11 +332,14 @@ public class OpenAPI2TDLTranslatorNext extends AbstractTranslator {
	}

	/**
     * Helper method for parsing specs into an intermediary OpenAPI structure for pre-processing.
	 * Helper method for parsing specs into an intermediary OpenAPI structure for
	 * pre-processing.
	 *
     * Use this method only for tests targeting processing helpers such as {@link org.openapitools.codegen.utils.ModelUtils}
     * or {@link InlineModelResolver}. Using this for testing generators will mean you're not testing the OpenAPI document
     * in a state the generator will be presented at runtime.
	 * Use this method only for tests targeting processing helpers such as
	 * {@link org.openapitools.codegen.utils.ModelUtils} or
	 * {@link InlineModelResolver}. Using this for testing generators will mean
	 * you're not testing the OpenAPI document in a state the generator will be
	 * presented at runtime.
	 *
	 * @param specFilePath The path to the specification file
	 * @return A "raw" OpenAPI document
@@ -309,4 +360,88 @@ public class OpenAPI2TDLTranslatorNext extends AbstractTranslator {
		return openAPI;
	}

	public void addMappings(String filename, Package mappingsPackage, DataResourceMapping resourceMapping, Package javaPackage) {
		
		CodegenConfigurator configurator = new CodegenConfigurator();
		// We create our own but the setting is required by the configurator
		configurator.setGeneratorName("java");
		configurator.setInputSpec(filename);
		// Set rest of configuration options directly to config (JavaClientCodegen)

		// Originally...
		// ClientOptInput clientOptInput = configurator.toClientOptInput();
		CodegenConfig config = new JavaClientCodegen() {
			@Override
			public Map<String, Object> postProcessSupportingFileData(Map<String, Object> objs) {

				AnnotationType getterAnnotation = null;
				AnnotationType setterAnnotation = null;
				if (javaPackage != null) {
					for (PackageableElement e : javaPackage.getPackagedElement()) {
						if (e.eClass().equals(tdlPackage.eINSTANCE.getAnnotationType())) {
							if (e.getName().equals("JavaGetter"))
								getterAnnotation = (AnnotationType) e;
							if (e.getName().equals("JavaSetter"))
								setterAnnotation = (AnnotationType) e;
						}
					}
				}
				
				List<ModelMap> allModels = (List<ModelMap>) objs.get("models");
				for (ModelMap modelMap : allModels) {
					CodegenModel codegen = modelMap.getModel();
					String name = codegen.getName();
					StructuredDataType type = getStructuredDataTypeFor(name);
					
					DataElementMapping eMapping = tdlFactory.eINSTANCE.createDataElementMapping();
					mappingsPackage.getPackagedElement().add(eMapping);
					eMapping.setName(getCleanName(name) + "_Mapping");
					eMapping.setMappableDataElement(type);
					eMapping.setElementURI(codegen.getClassname());
					eMapping.setDataResourceMapping(resourceMapping);
					
					for (CodegenProperty var: codegen.getVars()) {
						String vName = var.getBaseName();
						Member member = null;
						for (Member m: type.allMembers()) {
							if (m.getName().equals(getCleanName(vName))) {
								 member = m;
								 break;
							}
						}
						if (member == null)
							continue;
						
						ParameterMapping pMapping = tdlFactory.eINSTANCE.createParameterMapping();
						eMapping.getParameterMapping().add(pMapping);
						pMapping.setParameter(member);
						pMapping.setParameterURI(var.getName());

						if (getterAnnotation != null && setterAnnotation != null) {
							Annotation ga = tdlFactory.eINSTANCE.createAnnotation();
							ga.setKey(getterAnnotation);
							ga.setValue(var.getGetter());
							pMapping.getAnnotation().add(ga);
							Annotation sa = tdlFactory.eINSTANCE.createAnnotation();
							sa.setKey(setterAnnotation);
							sa.setValue(var.getSetter());
							pMapping.getAnnotation().add(sa);
						}
						
					}
				}
				//return super.postProcessSupportingFileData(objs);
				return objs;
			}
		};
		// TODO configure if needed
		ClientOptInput input = new ClientOptInput().config(config);
		//input.userDefinedTemplates(userDefinedTemplates);
		input.openAPI((OpenAPI) configurator.toContext().getSpecDocument());

		DefaultGenerator generator = new DefaultGenerator(true);
		generator.opts(input);
		generator.generate();
	}

}
+7 −1
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@ import org.eclipse.jface.preference.BooleanFieldEditor;
import org.eclipse.jface.preference.FieldEditor;
import org.eclipse.jface.preference.FieldEditorPreferencePage;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.StringFieldEditor;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.IWorkbenchPropertyPage;
@@ -20,7 +21,8 @@ import org.eclipse.ui.preferences.ScopedPreferenceStore;
public class PropertyPage extends FieldEditorPreferencePage implements IWorkbenchPropertyPage {
	
	private static final String PREFIX = "Openapi2tdl.";
	public static final String PROCEDURE_BASED = PREFIX + "ProcedureBased";
	public static final String PROCEDURE_BASED = PREFIX + "ProcedureBased",
			JAVA_API_PACKAGE = "JavaApiPackage";

	public static final String PREFERENCE_SCOPE = "org.etsi.mts.tdl.openapi2tdl";

@@ -99,6 +101,10 @@ public class PropertyPage extends FieldEditorPreferencePage implements IWorkbenc
		BooleanFieldEditor procBased = new BooleanFieldEditor(PROCEDURE_BASED, "Procedure-based API", parent);
		addField(procBased);

		parent = getFieldEditorParent();
		StringFieldEditor javaApiPackage = new StringFieldEditor(JAVA_API_PACKAGE, "Java API package", parent);
		addField(javaApiPackage);
		
		// TODO
	}