package ameba.mvc.template.internal; import com.google.common.collect.Collections2; import com.google.common.collect.Lists; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.glassfish.jersey.internal.util.PropertiesHelper; import org.glassfish.jersey.internal.util.collection.Ref; import org.glassfish.jersey.internal.util.collection.Refs; import org.glassfish.jersey.message.internal.MediaTypes; import org.glassfish.jersey.message.internal.VariantSelector; import org.glassfish.jersey.server.ContainerRequest; import org.glassfish.jersey.server.ExtendedUriInfo; import org.glassfish.jersey.server.mvc.MvcFeature; import org.glassfish.jersey.server.mvc.Template; import org.glassfish.jersey.server.mvc.Viewable; import javax.ws.rs.Produces; import javax.ws.rs.core.Configuration; import javax.ws.rs.core.FeatureContext; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Variant; import java.lang.annotation.Annotation; import java.nio.charset.Charset; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; /** * <p>TemplateHelper class.</p> * * @author icode * */ public class TemplateHelper { /** * Constant <code>TPL_ENGINE_DIR_PR="template.directory.engine."</code> */ public static final String TPL_ENGINE_DIR_PR = "template.directory.engine."; /** Constant <code>TPL_DIR="template.directory"</code> */ public static final String TPL_DIR = "template.directory"; private static final Charset DEFAULT_ENCODING = Charset.forName("UTF-8"); private TemplateHelper() { } /** * Return an absolute path to the given class where segments are separated using {@code delim} character and {@code path} * is appended to this path. * * @param resourceClass class for which an absolute path should be obtained. * @param path segment to be appended to the resulting path. * @param delim character used for separating path segments. * @return an absolute path to the resource class. */ public static String getAbsolutePath(Class<?> resourceClass, String path, char delim) { return '/' + resourceClass.getName().replace('.', '/').replace('$', delim) + delim + path; } /** * Get media types for which the {@link org.glassfish.jersey.server.mvc.spi.ResolvedViewable resolved viewable} could be * produced. * * @param containerRequest request to obtain acceptable media types. * @param extendedUriInfo uri info to obtain resource method from and its producible media types. * @param varyHeaderValue Vary header reference. * @return list of producible media types. */ public static List<MediaType> getProducibleMediaTypes(final ContainerRequest containerRequest, final ExtendedUriInfo extendedUriInfo, final Ref<String> varyHeaderValue) { final List<MediaType> producedTypes = getResourceMethodProducibleTypes(extendedUriInfo); final MediaType[] mediaTypes = producedTypes.toArray(new MediaType[producedTypes.size()]); final List<Variant> variants = VariantSelector.selectVariants(containerRequest, Variant.mediaTypes(mediaTypes) .build(), varyHeaderValue == null ? Refs.emptyRef() : varyHeaderValue); return Lists.transform(variants, variant -> MediaTypes.stripQualityParams(variant.getMediaType())); } /** * Get template name from given {@link org.glassfish.jersey.server.mvc.Viewable viewable} or return {@code index} if the given * viewable doesn't contain a valid template name. * * @param viewable viewable to obtain template name from. * @return {@code non-null}, {@code non-empty} template name. */ public static String getTemplateName(final Viewable viewable) { return viewable.getTemplateName() == null || viewable.getTemplateName().isEmpty() ? "index" : viewable.getTemplateName(); } /** * Return a list of producible media types of the last matched resource method. * * @param extendedUriInfo uri info to obtain resource method from. * @return list of producible media types of the last matched resource method. */ private static List<MediaType> getResourceMethodProducibleTypes(final ExtendedUriInfo extendedUriInfo) { if (extendedUriInfo.getMatchedResourceMethod() != null && !extendedUriInfo.getMatchedResourceMethod().getProducedTypes().isEmpty()) { return extendedUriInfo.getMatchedResourceMethod().getProducedTypes(); } return Collections.singletonList(MediaType.WILDCARD_TYPE); } /** * Extract {@link org.glassfish.jersey.server.mvc.Template template} annotation from given list. * * @param annotations list of annotations. * @return {@link org.glassfish.jersey.server.mvc.Template template} annotation or {@code null} if this annotation is not present. */ public static Template getTemplateAnnotation(final Annotation[] annotations) { if (annotations != null && annotations.length > 0) { for (Annotation annotation : annotations) { if (annotation instanceof Template) { return (Template) annotation; } } } return null; } /** * <p>getProduces.</p> * * @param annotations an array of {@link java.lang.annotation.Annotation} objects. * @return an array of {@link java.lang.String} objects. */ public static String[] getProduces(final Annotation[] annotations) { if (annotations != null && annotations.length > 0) { for (Annotation annotation : annotations) { if (annotation instanceof Produces) { return ((Produces) annotation).value(); } } } return null; } /** * Get output encoding from configuration. * * @param configuration Configuration. * @param suffix Template processor suffix of the * to configuration property {@link org.glassfish.jersey.server.mvc.MvcFeature#ENCODING}. * @return Encoding read from configuration properties or a default encoding if no encoding is configured. */ public static Charset getTemplateOutputEncoding(Configuration configuration, String suffix) { final String enc = PropertiesHelper.getValue(configuration.getProperties(), MvcFeature.ENCODING + suffix, String.class, null); if (enc == null) { return DEFAULT_ENCODING; } else { return Charset.forName(enc); } } /** * <p>getBasePaths.</p> * * @param basePath a {@link java.lang.String} object. * @return a {@link java.util.Collection} object. */ public static Collection<String> getBasePaths(String basePath) { return Collections2.transform(Lists.newArrayList(basePath.split(",")), s -> s.startsWith("/") ? s.substring(1) : s); } /** * <p>getExtends.</p> * * @param config a {@link javax.ws.rs.core.Configuration} object. * @param module a {@link java.lang.String} object. * @param defaultExtensions an array of {@link java.lang.String} objects. * @return an array of {@link java.lang.String} objects. */ public static String[] getExtends(Configuration config, String module, String[] defaultExtensions) { String extension = getStringExtends(config, module); if (StringUtils.isBlank(extension)) { return defaultExtensions; } else { extension = extension.toLowerCase(); } String[] extensions = extension.split(","); for (String ext : defaultExtensions) { if (!ArrayUtils.contains(extensions, ext.substring(1)) && !ArrayUtils.contains(extensions, ext)) { extensions = ArrayUtils.add(extensions, ext); } } return extensions; } /** * <p>getStringExtends.</p> * * @param config a {@link javax.ws.rs.core.Configuration} object. * @param module a {@link java.lang.String} object. * @return a {@link java.lang.String} object. */ public static String getStringExtends(Configuration config, String module) { Map<String, Object> map = config.getProperties(); String extension = (String) map.get(AbstractTemplateProcessor.TEMPLATE_CONF_PREFIX + module + ".suffix"); if (StringUtils.isNotBlank(extension)) { extension = StringUtils.deleteWhitespace(extension); } if (StringUtils.isBlank(extension)) { extension = (String) map.get(AbstractTemplateProcessor.TEMPLATE_CONF_PREFIX + "suffix"); if (StringUtils.isNotBlank(extension)) { extension = StringUtils.deleteWhitespace(extension); } } return StringUtils.isBlank(extension) ? null : extension.toLowerCase(); } /** * <p>getTemplateEngineDirConfig.</p> * * @param value a {@link java.lang.String} object. * @param engine a {@link java.lang.String} object. * @param context a {@link javax.ws.rs.core.FeatureContext} object. * @param tempConf a {@link java.util.Map} object. * @return a {@link java.lang.String} object. */ public static String getTemplateEngineDirConfig(String value, String engine, FeatureContext context, Map<String, String> tempConf) { if (StringUtils.isBlank(value)) { value = tempConf.get(TPL_ENGINE_DIR_PR + engine); } if (StringUtils.isBlank(value)) { value = (String) context.getConfiguration().getProperty(TPL_ENGINE_DIR_PR + engine); } if (StringUtils.isBlank(value)) { value = (String) context.getConfiguration().getProperty(TPL_DIR); } return value; } /** * <p>getBasePath.</p> * * @param properties a {@link java.util.Map} object. * @param cfgSuffix a {@link java.lang.String} object. * @return a {@link java.lang.String} object. */ public static String getBasePath(Map<String, Object> properties, String cfgSuffix) { String basePath = PropertiesHelper.getValue(properties, MvcFeature.TEMPLATE_BASE_PATH + "." + cfgSuffix, String.class, null); if (basePath == null) { basePath = PropertiesHelper.getValue(properties, MvcFeature.TEMPLATE_BASE_PATH, "", null); } return basePath; } }