Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[hb] Honor supporting files when applying bundles #5364

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,38 +16,88 @@

package org.openapitools.codegen.api;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Locale;
import java.util.Map;

/**
* Each templating engine is called by an Adapter, selected at runtime
*/
public interface TemplatingEngineAdapter{

/**
* Provides an identifier used to load the adapter. This could be a name, uuid, or any other string.
*
* @return A string identifier.
*/
String getIdentifier();

/**
* Compiles a template into a string
*
* @param generator From where we can fetch the templates content (e.g. an instance of DefaultGenerator)
* @param bundle The map of values to pass to the template
* @param templateFile The name of the template (e.g. model.mustache )
* @return the processed template result
* @throws IOException an error ocurred in the template processing
*/
String compileTemplate(TemplatingGenerator generator, Map<String, Object> bundle,
String templateFile) throws IOException;

/**
* During generation, if a supporting file has a file extension that is
* inside that array, then it is considered a templated supporting file
* and we use the templating engine adapter to generate it
* @return string array of the valid file extensions for this templating engine
*/
String[] getFileExtensions();
public interface TemplatingEngineAdapter {

/**
* Provides an identifier used to load the adapter. This could be a name, uuid, or any other string.
*
* @return A string identifier.
*/
String getIdentifier();

/**
* Compiles a template into a string
*
* @param generator From where we can fetch the templates content (e.g. an instance of DefaultGenerator)
* @param bundle The map of values to pass to the template
* @param templateFile The name of the template (e.g. model.mustache )
* @return the processed template result
* @throws IOException an error ocurred in the template processing
*/
String compileTemplate(TemplatingGenerator generator, Map<String, Object> bundle,
String templateFile) throws IOException;

/**
* During generation, if a supporting file has a file extension that is
* inside that array, then it is considered a templated supporting file
* and we use the templating engine adapter to generate it
*
* @return string array of the valid file extensions for this templating engine
*/
String[] getFileExtensions();

/**
* Determines whether the template file with supported extensions exists. This may be on the filesystem,
* external filesystem, or classpath (implementation is up to TemplatingGenerator).
*
* @param generator The generator holding details about file resolution
* @param templateFile The original target filename
* @return True if the template is available in the template search path, false if it can not be found
*/
default boolean templateExists(TemplatingGenerator generator, String templateFile) {
return Arrays.stream(getFileExtensions()).anyMatch(ext -> {
int idx = templateFile.lastIndexOf(".");
String baseName;
if (idx > 0 && idx < templateFile.length() - 1) {
baseName = templateFile.substring(0, idx);
} else {
baseName = templateFile;
}

Path path = generator.getFullTemplatePath(String.format(Locale.ROOT, "%s.%s", baseName, ext));

InputStream is = null;
try {
String resourcePath = System.getProperty("os.name").startsWith("Windows") ?
path.toString().replace("\\", "/") :
path.toString();
is = this.getClass().getClassLoader().getResourceAsStream(resourcePath);
if (is == null) {
is = new FileInputStream(path.toFile());
}

return is.available() > 0;
} catch (IOException e) {
// ignore
} finally {
try {
if (is != null) is.close();
} catch (IOException e) {
// ignore
}
}
return false;
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,24 @@

/**
* interface to the full template content
* implementers might take into account the -t cli option,
* look in the resources for a language specific template, etc
* implementers might take into account the -t cli option,
* look in the resources for a language specific template, etc
*/
public interface TemplatingGenerator {

/**
* returns the template content by name
*
* @param name the template name (e.g. model.mustache)
*
* @return the contents of that template
*/
String getFullTemplateContents(String name);
/**
* returns the template content by name
*
* @param name the template name (e.g. model.mustache)
* @return the contents of that template
*/
String getFullTemplateContents(String name);

/**
* Returns the path of a template, allowing access to the template where consuming literal contents aren't desirable or possible.
*
* @param name the template name (e.g. model.mustache)
*
* @return The {@link Path} to the template
*/
Path getFullTemplatePath(String name);
/**
* Returns the path of a template, allowing access to the template where consuming literal contents aren't desirable or possible.
*
* @param name the template name (e.g. model.mustache)
* @return The {@link Path} to the template
*/
Path getFullTemplatePath(String name);
}
Original file line number Diff line number Diff line change
Expand Up @@ -723,7 +723,9 @@ private void generateSupportingFiles(List<File> files, Map<String, Object> bundl
}

if (ignoreProcessor.allowsFile(new File(outputFilename))) {
if (Arrays.stream(templatingEngine.getFileExtensions()).anyMatch(templateFile::endsWith)) {
// support.templateFile is the unmodified/original supporting file name (e.g. build.sh.mustache)
// templatingEngine.templateExists dispatches resolution to this, performing template-engine specific inspect of support file extensions.
if (templatingEngine.templateExists(this, support.templateFile)) {
String templateContent = templatingEngine.compileTemplate(this, bundle, support.templateFile);
writeToFile(outputFilename, templateContent);
File written = new File(outputFilename);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ public TemplateSource findTemplate(TemplatingGenerator generator, String templat
} catch (Exception ignored) {
}
}
throw new RuntimeException("couldnt find a subtemplate " + templateFile);
throw new TemplateNotFoundException(templateFile);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public Reader findTemplate(TemplatingGenerator generator, String name) {
} catch (Exception ignored) {
}
}
throw new RuntimeException("couldnt find a subtemplate " + name);
throw new TemplateNotFoundException(name);
}

public Mustache.Compiler getCompiler() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package org.openapitools.codegen.templating;

public class TemplateNotFoundException extends RuntimeException {
/**
* Constructs a new runtime exception with {@code null} as its
* detail message. The cause is not initialized, and may subsequently be
* initialized by a call to {@link #initCause}.
*/
public TemplateNotFoundException() {
}

/**
* Constructs a new runtime exception with the specified detail message.
* The cause is not initialized, and may subsequently be initialized by a
* call to {@link #initCause}.
*
* @param message the detail message. The detail message is saved for
* later retrieval by the {@link #getMessage()} method.
*/
public TemplateNotFoundException(String message) {
super(message);
}

/**
* Constructs a new runtime exception with the specified detail message and
* cause. <p>Note that the detail message associated with
* {@code cause} is <i>not</i> automatically incorporated in
* this runtime exception's detail message.
*
* @param message the detail message (which is saved for later retrieval
* by the {@link #getMessage()} method).
* @param cause the cause (which is saved for later retrieval by the
* {@link #getCause()} method). (A <code>null</code> value is
* permitted, and indicates that the cause is nonexistent or
* unknown.)
* @since 1.4
*/
public TemplateNotFoundException(String message, Throwable cause) {
super(message, cause);
}

/**
* Constructs a new runtime exception with the specified cause and a
* detail message of <code>(cause==null ? null : cause.toString())</code>
* (which typically contains the class and detail message of
* <code>cause</code>). This constructor is useful for runtime exceptions
* that are little more than wrappers for other throwables.
*
* @param cause the cause (which is saved for later retrieval by the
* {@link #getCause()} method). (A <code>null</code> value is
* permitted, and indicates that the cause is nonexistent or
* unknown.)
* @since 1.4
*/
public TemplateNotFoundException(Throwable cause) {
super(cause);
}

/**
* Constructs a new runtime exception with the specified detail
* message, cause, suppression enabled or disabled, and writable
* stack trace enabled or disabled.
*
* @param message the detail message.
* @param cause the cause. (A {@code null} value is permitted,
* and indicates that the cause is nonexistent or unknown.)
* @param enableSuppression whether or not suppression is enabled
* or disabled
* @param writableStackTrace whether or not the stack trace should
* be writable
* @since 1.7
*/
public TemplateNotFoundException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}