Skip to content

Commit

Permalink
Provide Maven plugin for generating TypeScript DTO objects based on J…
Browse files Browse the repository at this point in the history
…ava DTO dependencies

It allow clients of REST API of Eclipse Che to use a Typed variables instead of dynamic variables for retreieved JSON
(It includes Dashboard and Chefile CLI and other clients based on TypeScript)

input : <maven DTO modules>
ouput : dto.ts file including DTO definition and an implementation for building objects around JSON content

Change-Id: I5eb2e2a958ffb99fc63dc6ff606a528ad105d7e0
Signed-off-by: Florent BENOIT <fbenoit@codenvy.com>
  • Loading branch information
benoitf committed Sep 22, 2016
1 parent 3fc6345 commit b217f9a
Show file tree
Hide file tree
Showing 14 changed files with 1,432 additions and 0 deletions.
126 changes: 126 additions & 0 deletions core/che-core-typescript-dto-maven-plugin/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2012-2016 Codenvy, S.A.
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
which accompanies this distribution, and is available at
http://www.eclipse.org/legal/epl-v10.html
Contributors:
Codenvy, S.A. - initial API and implementation
-->
<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">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>che-core-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>5.0.0-M2-SNAPSHOT</version>
</parent>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-typescript-dto-maven-plugin</artifactId>
<packaging>maven-plugin</packaging>
<name>Che Core :: API :: TypeScript DTO maven plugin</name>
<dependencies>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>org.antlr</groupId>
<artifactId>ST4</artifactId>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-core</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-dto</artifactId>
</dependency>
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-artifact</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-model</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.sisu</groupId>
<artifactId>org.eclipse.sisu.plexus</artifactId>
<scope>provided</scope>
<exclusions>
<exclusion>
<artifactId>jsr250-api</artifactId>
<groupId>javax.annotation</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-compat</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.maven.plugin-testing</groupId>
<artifactId>maven-plugin-testing-harness</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-utils</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugin-plugin</artifactId>
<executions>
<execution>
<id>mojo-descriptor</id>
<goals>
<goal>descriptor</goal>
</goals>
</execution>
</executions>
<configuration>
<skipErrorNoDescriptorsFound>true</skipErrorNoDescriptorsFound>
</configuration>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.plugin.typescript.dto;

import org.eclipse.che.dto.shared.DelegateTo;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

/**
* Helper class
*
* @author Florent Benoit
*/
public class DTOHelper {

/**
* Utility class.
*/
private DTOHelper() {

}

/**
* Check is specified method is DTO getter.
*/
public static boolean isDtoGetter(Method method) {
if (method.isAnnotationPresent(DelegateTo.class)) {
return false;
}

if (method.getParameterTypes().length > 0) {
return false;
}

String methodName = method.getName();

return methodName.startsWith("get") ||
(methodName.startsWith("is") && ((method.getReturnType() == Boolean.class || method.getReturnType() == boolean.class)));

}


/**
* Check is specified method is DTO setter.
*/
public static boolean isDtoSetter(Method method) {
if (method.isAnnotationPresent(DelegateTo.class)) {
return false;
}
String methodName = method.getName();
return methodName.startsWith("set") && method.getParameterTypes().length == 1;
}

/**
* Check is specified method is DTO with.
*/
public static boolean isDtoWith(Method method) {
if (method.isAnnotationPresent(DelegateTo.class)) {
return false;
}
String methodName = method.getName();
return methodName.startsWith("with") && method.getParameterTypes().length == 1;
}

/**
* Compute field name from the stringified string type
*/
public static String getFieldName(String type) {
char[] c = type.toCharArray();
c[0] = Character.toLowerCase(c[0]);
return new String(c);
}

/**
* Extract field name from the getter method
*/
public static String getGetterFieldName(Method method) {
String methodName = method.getName();
if (methodName.startsWith("get")) {
return getFieldName(methodName.substring(3));
} else if (methodName.startsWith("is")) {
return getFieldName(methodName.substring(2));
}
throw new IllegalArgumentException("Invalid getter method" + method.getName());
}

/**
* Extract field name from the setter method
*/
public static String getSetterFieldName(Method method) {
String methodName = method.getName();
if (methodName.startsWith("set")) {
return getFieldName(methodName.substring(3));
}
throw new IllegalArgumentException("Invalid setter method" + method.getName());
}

/**
* Extract field name from the with method
*/
public static String getWithFieldName(Method method) {
String methodName = method.getName();
if (methodName.startsWith("with")) {
return getFieldName(methodName.substring(4));
}
throw new IllegalArgumentException("Invalid with method" + method.getName());
}

/**
* Convert Java type to TypeScript type
*/
public static String convertType(Type type) {
if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType)type;
Type rawType = parameterizedType.getRawType();
return convertParametrizedType(type, parameterizedType, rawType);
} else if (String.class.equals(type) || (type instanceof Class && ((Class)type).isEnum())) {
// Maybe find a better enum type for typescript
return "string";
} else if (Integer.class.equals(type) || Integer.TYPE.equals(type) || Long.class.equals(type) || Long.TYPE.equals(type)) {
return "number";
} else if (Boolean.class.equals(type)) {
return "boolean";
}

return type.getTypeName();
}

/**
* Handle convert of a parametrized Java type to TypeScript type
*/
public static String convertParametrizedType(Type type, ParameterizedType parameterizedType, Type rawType) {

if (List.class.equals(rawType)) {
return "Array<" + convertType(parameterizedType.getActualTypeArguments()[0]) + ">";
} else if (Map.class.equals(rawType)) {
return "Map<" + convertType(parameterizedType.getActualTypeArguments()[0]) + "," +
convertType(parameterizedType.getActualTypeArguments()[1]) + ">";
} else {
throw new IllegalArgumentException("Invalid type" + type);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.plugin.typescript.dto;

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectHelper;

import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;

/**
* Mojo for generating TypeScript DTO interface + implementation for handling JSON data.
* @author Florent Benoit
*/
@Mojo(name = "build",
defaultPhase = LifecyclePhase.PACKAGE,
requiresProject = true,
requiresDependencyCollection = ResolutionScope.RUNTIME)
public class TypeScriptDTOGeneratorMojo extends AbstractMojo {

/**
* Project providing artifact id, version and dependencies.
*/
@Parameter(defaultValue = "${project}", readonly = true)
private MavenProject project;

/**
* build directory used to write the intermediate bom file.
*/
@Parameter(defaultValue = "${project.build.directory}")
private File targetDirectory;

@Component
private MavenProjectHelper projectHelper;

/**
* Path to the generated typescript file
*/
private File typescriptFile;

/**
* Use of classpath instead of classloader
*/
private boolean useClassPath;

@Override
public void execute() throws MojoExecutionException, MojoFailureException {

getLog().info("Generating TypeScript DTO");

TypeScriptDtoGenerator typeScriptDtoGenerator = new TypeScriptDtoGenerator();

typeScriptDtoGenerator.setUseClassPath(useClassPath);

// define output path for the file to write with typescript definition
String output = typeScriptDtoGenerator.execute();

this.typescriptFile = new File(targetDirectory, project.getArtifactId() + ".ts");
File parentDir = this.typescriptFile.getParentFile();
if (!parentDir.exists() && !parentDir.mkdirs()) {
throw new MojoExecutionException("Unable to create a directory for writing DTO typescript file '" + parentDir + "'.");
}

try (Writer fileWriter = Files.newBufferedWriter(this.typescriptFile.toPath(), StandardCharsets.UTF_8)) {
fileWriter.write(output);
} catch (IOException e) {
throw new MojoExecutionException("Cannot write DTO typescript file");
}

// attach this typescript file as maven artifact
projectHelper.attachArtifact(project, "ts", typescriptFile);
}

/**
* Gets the TypeScript generated file
* @return the generated file TypeScript link
*/
public File getTypescriptFile() {
return typescriptFile;
}

/**
* Allow to configure generator to use classpath instead of classloader
* @param useClassPath true if want to use classpath loading
*/
public void setUseClassPath(boolean useClassPath) {
this.useClassPath = useClassPath;
}


}
Loading

0 comments on commit b217f9a

Please sign in to comment.