Skip to content
Permalink
Browse files
FREEMARKER-172 [freemarker-generator] Refactor DataSourceFactory (#27)
  • Loading branch information
sgoeschl committed Feb 6, 2021
1 parent 4962dcb commit f0bae9a8870f325fa828afcd5968169516648944
Showing 39 changed files with 960 additions and 377 deletions.
@@ -34,7 +34,10 @@
import java.io.StringWriter;
import java.net.URI;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Objects.requireNonNull;
@@ -43,7 +46,7 @@

/**
* Data source which encapsulates data to be used for rendering
* a template. When accessing content it is loaded on demand on not
* a template. When accessing content it is loaded on demand and not
* kept in memory to allow processing of large volumes of data.
* <br>
* There is also special support of <code>UrlDataSource</code> since
@@ -55,14 +58,25 @@
*/
public class DataSource implements Closeable, javax.activation.DataSource {

public static final String METADATA_BASE_NAME = "baseName";
public static final String METADATA_BASE_NAME = "basename";
public static final String METADATA_EXTENSION = "extension";
public static final String METADATA_FILE_NAME = "fileName";
public static final String METADATA_FILE_PATH = "filePath";
public static final String METADATA_FILE_NAME = "filename";
public static final String METADATA_FILE_PATH = "filepath";
public static final String METADATA_GROUP = "group";
public static final String METADATA_NAME = "name";
public static final String METADATA_URI = "uri";
public static final String METADATA_URI_PATH = "uriPath";
public static final String METADATA_MIME_TYPE = "mimetype";

public static final List<String> METADATA_KEYS = Arrays.asList(
METADATA_BASE_NAME,
METADATA_EXTENSION,
METADATA_FILE_NAME,
METADATA_FILE_PATH,
METADATA_GROUP,
METADATA_NAME,
METADATA_URI,
METADATA_MIME_TYPE
);

/** Human-readable name of the data source */
private final String name;
@@ -140,14 +154,32 @@ public String getGroup() {
return group;
}

/**
* Get the file name from the underlying "FileDataSource". All
* other data sources will return an empty string.
*
* @return file name or empty string
*/
public String getFileName() {
return FilenameUtils.getName(name);
return isFileDataSource() ? FilenameUtils.getName(dataSource.getName()) : "";
}

/**
* Get the base name from the underlying "FileDataSource". All
* other data sources will return an empty string.
*
* @return base name or empty string
*/
public String getBaseName() {
return FilenameUtils.getBaseName(getFileName());
}

/**
* Get the extension from the underlying "FileDataSource". All
* other data sources will return an empty string.
*
* @return base name or empty string
*/
public String getExtension() {
return FilenameUtils.getExtension(getFileName());
}
@@ -180,11 +212,11 @@ public URI getUri() {
* @return Length of data source or UNKNOWN_LENGTH
*/
public long getLength() {
if (dataSource instanceof FileDataSource) {
if (isFileDataSource()) {
return ((FileDataSource) dataSource).getFile().length();
} else if (dataSource instanceof StringDataSource) {
} else if (isStringDataSource()) {
return ((StringDataSource) dataSource).length();
} else if (dataSource instanceof ByteArrayDataSource) {
} else if (isByteArrayDataSource()) {
return ((ByteArrayDataSource) dataSource).length();
} else {
return DATASOURCE_UNKNOWN_LENGTH;
@@ -220,7 +252,7 @@ public String getText(String charsetName) {
}

/**
* Gets the contents of an <code>InputStream</code> as a list of Strings,
* Get the content of an <code>InputStream</code> as a list of Strings,
* one entry per line, using the specified character encoding.
*
* @return the list of Strings, never null
@@ -246,7 +278,7 @@ public List<String> getLines(String charsetName) {
}

/**
* Returns an Iterator for the lines in an <code>InputStream</code>, using
* Returns an iterator for the lines in an <code>InputStream</code>, using
* the default character encoding specified. The exposed iterator is closed
* by the <code>DataSource</code>.
*
@@ -282,7 +314,7 @@ public byte[] getBytes() {
}

/**
* Expose various parts of the metadata as simple strings to cater for filtering in a script.
* Expose various parts of the metadata as simple strings to cater for filtering in a script.
*
* @param key key part key
* @return value
@@ -302,15 +334,25 @@ public String getMetadata(String key) {
return getGroup();
case METADATA_NAME:
return getName();
case METADATA_URI_PATH:
return uri.getPath();
case METADATA_URI:
return uri.toString();
case METADATA_MIME_TYPE:
return getMimeType();
default:
throw new IllegalArgumentException("Unknown key: " + key);
}
}

/**
* Get all metadata parts as map.
*
* @return Map of metadata parts
*/
public Map<String, String> getMetadata() {
return METADATA_KEYS.stream()
.collect(Collectors.toMap(key -> key, this::getMetadata));
}

/**
* Matches a metadata key with a wildcard expression.
*
@@ -348,15 +390,29 @@ public String toString() {

/**
* If there is no content type we ask the underlying data source. E.g. for
* an URL data source this information is fetched from the remote server.
* an <code>URLDataSource</code> this information is fetched from the
* remote server.
*
* @return content type
*/
private String contentType() {
if (StringUtils.isNotEmpty(contentType)) {
return contentType;
} else {
return StringUtils.firstNonEmpty(dataSource.getContentType(), MIME_APPLICATION_OCTET_STREAM);
final String contentType = dataSource.getContentType();
return StringUtils.firstNonEmpty(contentType, MIME_APPLICATION_OCTET_STREAM);
}
}
}

private boolean isFileDataSource() {
return dataSource instanceof FileDataSource;
}

private boolean isStringDataSource() {
return dataSource instanceof StringDataSource;
}

private boolean isByteArrayDataSource() {
return dataSource instanceof ByteArrayDataSource;
}
}
@@ -22,10 +22,7 @@
import org.apache.freemarker.generator.base.activation.InputStreamDataSource;
import org.apache.freemarker.generator.base.activation.StringDataSource;
import org.apache.freemarker.generator.base.mime.MimetypesFileTypeMapFactory;
import org.apache.freemarker.generator.base.uri.NamedUri;
import org.apache.freemarker.generator.base.uri.NamedUriStringParser;
import org.apache.freemarker.generator.base.util.PropertiesFactory;
import org.apache.freemarker.generator.base.util.StringUtils;
import org.apache.freemarker.generator.base.util.UriUtils;
import org.apache.freemarker.generator.base.util.Validate;

@@ -44,61 +41,26 @@

import static java.nio.charset.StandardCharsets.UTF_8;
import static org.apache.freemarker.generator.base.FreeMarkerConstants.DEFAULT_GROUP;
import static org.apache.freemarker.generator.base.mime.Mimetypes.MIME_TEXT_PLAIN;
import static org.apache.freemarker.generator.base.util.StringUtils.firstNonEmpty;

/**
* Creates a FreeMarker data source from various sources.
* Low-level factory to create FreeMarker data sources.
*/
public abstract class DataSourceFactory {

private static final String NO_MIME_TYPE = null;
private static final Charset NO_CHARSET = null;
private static final String ROOT_DIR = "/";

private DataSourceFactory() {
}

// == NamedUri ==========================================================

public static DataSource fromNamedUri(String str) {
Validate.notNull(str, "namedUri is null");

return fromNamedUri(NamedUriStringParser.parse(str));
}
// == General ===========================================================

public static DataSource fromNamedUri(NamedUri namedUri) {
Validate.notNull(namedUri, "namedUri is null");

final URI uri = namedUri.getUri();
final String group = namedUri.getGroupOrElse(DEFAULT_GROUP);
final Charset charset = getCharsetOrElse(namedUri, NO_CHARSET);
final String mimeType = getMimeTypeOrElse(namedUri, NO_MIME_TYPE);

if (UriUtils.isHttpUri(uri)) {
final URL url = toUrl(uri);
final String name = namedUri.getNameOrElse(UriUtils.toStringWithoutFragment(uri));
return fromUrl(name, group, url, mimeType, charset);
} else if (UriUtils.isFileUri(uri)) {
final File file = namedUri.getFile();
final String name = namedUri.getNameOrElse(UriUtils.toStringWithoutFragment(file.toURI()));
return fromFile(name, group, file, charset);
} else if (UriUtils.isEnvUri(uri)) {
// environment variables come with a leading "/" to be removed
final String key = stripRootDir(uri.getPath());
final String contentType = getMimeTypeOrElse(namedUri, MIME_TEXT_PLAIN);
final String name = firstNonEmpty(namedUri.getName(), key, Location.ENVIRONMENT);
if (StringUtils.isEmpty(key)) {
return fromEnvironment(name, group, contentType);
} else {
return fromEnvironment(name, group, key, contentType);
}
} else {
// handle things such as "foo=some.file"
final File file = namedUri.getFile();
final String name = namedUri.getNameOrElse(UriUtils.toStringWithoutFragment(file.toURI()));
return fromFile(name, group, file, charset);
}
public static DataSource create(
String name,
String group,
URI uri,
javax.activation.DataSource dataSource,
String contentType,
Charset charset
) {
return new DataSource(name, group, uri, dataSource, contentType, charset);
}

// == URL ===============================================================
@@ -120,7 +82,7 @@ public static DataSource fromString(String name, String group, String content, S
// == File ==============================================================

public static DataSource fromFile(File file, Charset charset) {
return fromFile(UriUtils.toStringWithoutFragment(file.toURI()), DEFAULT_GROUP, file, charset);
return fromFile(file.getName(), DEFAULT_GROUP, file, charset);
}

public static DataSource fromFile(String name, String group, File file, Charset charset) {
@@ -143,12 +105,18 @@ public static DataSource fromBytes(String name, String group, byte[] content, St
// == InputStream =======================================================

public static DataSource fromInputStream(String name, String group, InputStream is, String contentType, Charset charset) {
final InputStreamDataSource dataSource = new InputStreamDataSource(name, is);
final URI uri = UriUtils.toUri(Location.INPUTSTREAM + ":///");
return create(name, group, uri, dataSource, contentType, charset);
return fromInputStream(name, group, uri, is, contentType, charset);
}

public static DataSource fromInputStream(String name, String group, URI uri, InputStream is, String contentType, Charset charset) {
public static DataSource fromInputStream(
String name,
String group,
URI uri,
InputStream is,
String contentType,
Charset charset
) {
final InputStreamDataSource dataSource = new InputStreamDataSource(name, is);
return create(name, group, uri, dataSource, contentType, charset);
}
@@ -164,7 +132,7 @@ public static DataSource fromEnvironment(String name, String group, String conte
final URI uri = UriUtils.toUri(Location.ENVIRONMENT, "");
return create(name, group, uri, dataSource, contentType, UTF_8);
} catch (IOException e) {
throw new RuntimeException(e);
throw new RuntimeException("Unable to load environment variables", e);
}
}

@@ -176,60 +144,11 @@ public static DataSource fromEnvironment(String name, String group, String key,
return create(name, group, uri, dataSource, contentType, UTF_8);
}

// == General ===========================================================

/**
* Create a data source based on a
* <ul>
* <li>URI</li>
* <li>Named URI</li>
* <li>file name</li>
* </ul>
*
* @param source source of the data source
* @return DataSource
*/
public static DataSource create(String source) {
if (UriUtils.isUri(source)) {
return fromNamedUri(source);
} else {
final File file = new File(source);
return fromFile(file.getName(), DEFAULT_GROUP, file, UTF_8);
}
}

public static DataSource create(
String name,
String group,
URI uri,
javax.activation.DataSource dataSource,
String contentType,
Charset charset) {
return new DataSource(name, group, uri, dataSource, contentType, charset);
}

private static String getMimeTypeOrElse(NamedUri namedUri, String def) {
return namedUri.getParameter(NamedUri.MIMETYPE, def);
}

private static Charset getCharsetOrElse(NamedUri namedUri, Charset def) {
final String charsetName = namedUri.getParameter(NamedUri.CHARSET);
return StringUtils.isEmpty(charsetName) ? def : Charset.forName(charsetName);
}

private static URL toUrl(URI uri) {
public static URL toUrl(String url) {
try {
return uri.toURL();
return new URL(url);
} catch (MalformedURLException e) {
throw new IllegalArgumentException(uri.toString(), e);
}
}

private static String stripRootDir(String str) {
if (str.startsWith(ROOT_DIR)) {
return str.substring(ROOT_DIR.length());
} else {
return str;
throw new IllegalArgumentException(url, e);
}
}
}

0 comments on commit f0bae9a

Please sign in to comment.