InlineCssTranslator.java

1
package fr.sii.ogham.html.translator;
2
3
import static fr.sii.ogham.core.util.HtmlUtils.getDistinctCssUrls;
4
import static fr.sii.ogham.core.util.HtmlUtils.isHtml;
5
import static fr.sii.ogham.core.util.HtmlUtils.skipExternalUrls;
6
import static fr.sii.ogham.html.inliner.impl.jsoup.CssInlineUtils.removeOghamAttributes;
7
8
import java.io.IOException;
9
import java.util.ArrayList;
10
import java.util.List;
11
12
import org.slf4j.Logger;
13
import org.slf4j.LoggerFactory;
14
15
import fr.sii.ogham.core.exception.handler.ContentTranslatorException;
16
import fr.sii.ogham.core.exception.handler.CssInliningException;
17
import fr.sii.ogham.core.exception.resource.ResourceResolutionException;
18
import fr.sii.ogham.core.message.content.Content;
19
import fr.sii.ogham.core.message.content.HasResourcePath;
20
import fr.sii.ogham.core.message.content.MayHaveStringContent;
21
import fr.sii.ogham.core.message.content.StringContent;
22
import fr.sii.ogham.core.message.content.UpdatableStringContent;
23
import fr.sii.ogham.core.resource.path.RelativePathResolver;
24
import fr.sii.ogham.core.resource.path.ResourcePath;
25
import fr.sii.ogham.core.resource.path.UnresolvedPath;
26
import fr.sii.ogham.core.resource.resolver.ResourceResolver;
27
import fr.sii.ogham.core.translator.content.ContentTranslator;
28
import fr.sii.ogham.core.util.IOUtils;
29
import fr.sii.ogham.html.inliner.CssInliner;
30
import fr.sii.ogham.html.inliner.ExternalCss;
31
32
/**
33
 * Translator that transforms HTML content. If not HTML, the translator has no
34
 * effect. The HTML is analyzed in order to find external css files. For each
35
 * found image, it uses the resource resolver in order to find the css file.
36
 * Once all css files are found, the HTML is transformed in order to inline the
37
 * styles.
38
 * 
39
 * @author Aurélien Baudet
40
 *
41
 */
42
public class InlineCssTranslator implements ContentTranslator {
43
	private static final Logger LOG = LoggerFactory.getLogger(InlineCssTranslator.class);
44
	
45
	/**
46
	 * The CSS inliner
47
	 */
48
	private final CssInliner cssInliner;
49
50
	/**
51
	 * The resource resolver to find the CSS files
52
	 */
53
	private final ResourceResolver resourceResolver;
54
	
55
	/**
56
	 * Provides an instance used to resolve relative path from source path and relative path
57
	 */
58
	private final RelativePathResolver relativePathProvider;
59
60
	public InlineCssTranslator(CssInliner cssInliner, ResourceResolver resourceResolver, RelativePathResolver relativePathProvider) {
61
		super();
62
		this.cssInliner = cssInliner;
63
		this.resourceResolver = resourceResolver;
64
		this.relativePathProvider = relativePathProvider;
65
	}
66
67
	@Override
68
	public Content translate(Content content) throws ContentTranslatorException {
69 2 1. translate : negated conditional → RUN_ERROR
2. translate : negated conditional → RUN_ERROR
		if (content instanceof MayHaveStringContent && ((MayHaveStringContent) content).canProvideString()) {
70
			String stringContent = ((MayHaveStringContent) content).asString();
71 1 1. translate : negated conditional → RUN_ERROR
			if (isHtml(stringContent)) {
72
				List<String> cssFiles = skipExternalUrls(getDistinctCssUrls(stringContent));
73 1 1. translate : negated conditional → RUN_ERROR
				if (!cssFiles.isEmpty()) {
74
					// prepare list of css files/urls with their content
75
					List<ExternalCss> cssResources = load(getSourcePath(content), cssFiles);
76
					// generate the content with inlined css
77
					String inlinedContentStr = cssInliner.inline(stringContent, cssResources);
78
					// remove ogham attributes
79
					String cleaned = clean(inlinedContentStr);
80
					// update the HTML content
81 1 1. translate : replaced return value with null for fr/sii/ogham/html/translator/InlineCssTranslator::translate → RUN_ERROR
					return updateHtmlContent(content, cleaned);
82
				}
83
			}
84
		} else {
85
			LOG.debug("Neither content as string nor HTML. Skip CSS inlining");
86
			LOG.trace("content: {}", content);
87
		}
88 1 1. translate : replaced return value with null for fr/sii/ogham/html/translator/InlineCssTranslator::translate → RUN_ERROR
		return content;
89
	}
90
91
	private static ResourcePath getSourcePath(Content content) {
92 1 1. getSourcePath : negated conditional → RUN_ERROR
		if(content instanceof HasResourcePath) {
93 1 1. getSourcePath : replaced return value with null for fr/sii/ogham/html/translator/InlineCssTranslator::getSourcePath → NO_COVERAGE
			return ((HasResourcePath) content).getPath();
94
		}
95 1 1. getSourcePath : replaced return value with null for fr/sii/ogham/html/translator/InlineCssTranslator::getSourcePath → RUN_ERROR
		return new UnresolvedPath("");
96
	}
97
98
	private List<ExternalCss> load(ResourcePath sourcePath, List<String> cssFiles) throws ContentTranslatorException {
99
		List<ExternalCss> cssResources = new ArrayList<>(cssFiles.size());
100
		for (String path : cssFiles) {
101 1 1. load : removed call to fr/sii/ogham/html/translator/InlineCssTranslator::load → RUN_ERROR
			load(cssResources, relativePathProvider.resolve(sourcePath, path));
102
		}
103 1 1. load : replaced return value with Collections.emptyList for fr/sii/ogham/html/translator/InlineCssTranslator::load → RUN_ERROR
		return cssResources;
104
	}
105
106
	private void load(List<ExternalCss> cssResources, ResourcePath path) throws ContentTranslatorException {
107
		try {
108
			cssResources.add(new ExternalCss(path, IOUtils.toString(resourceResolver.getResource(path).getInputStream())));
109
		} catch (IOException e) {
110
			throw new CssInliningException("Failed to inline CSS file " + path + " because it can't be read", e);
111
		} catch (ResourceResolutionException e) {
112
			throw new CssInliningException("Failed to inline CSS file " + path + " because it can't be resolved", e);
113
		}
114
	}
115
116
	private static Content updateHtmlContent(Content content, String inlinedContentStr) {
117
		Content inlinedContent = content;
118 1 1. updateHtmlContent : negated conditional → RUN_ERROR
		if(content instanceof UpdatableStringContent) {
119
			LOG.debug("Content is updatable => update it with inlined CSS");
120 1 1. updateHtmlContent : removed call to fr/sii/ogham/core/message/content/UpdatableStringContent::setStringContent → RUN_ERROR
			((UpdatableStringContent) inlinedContent).setStringContent(inlinedContentStr);
121
		} else {
122
			LOG.info("Content is not updatable => create a new StringContent for CSS inlining result");
123
			inlinedContent = new StringContent(inlinedContentStr);
124
		}
125 1 1. updateHtmlContent : replaced return value with null for fr/sii/ogham/html/translator/InlineCssTranslator::updateHtmlContent → RUN_ERROR
		return inlinedContent;
126
	}
127
	
128
	private static String clean(String content) {
129 1 1. clean : replaced return value with "" for fr/sii/ogham/html/translator/InlineCssTranslator::clean → RUN_ERROR
		return removeOghamAttributes(content);
130
	}
131
132
133
	@Override
134
	public String toString() {
135 1 1. toString : replaced return value with "" for fr/sii/ogham/html/translator/InlineCssTranslator::toString → RUN_ERROR
		return "InlineCssTranslator";
136
	}
137
	
138
}

Mutations

69

1.1
Location : translate
Killed by :
negated conditional → RUN_ERROR

2.2
Location : translate
Killed by :
negated conditional → RUN_ERROR

71

1.1
Location : translate
Killed by :
negated conditional → RUN_ERROR

73

1.1
Location : translate
Killed by :
negated conditional → RUN_ERROR

81

1.1
Location : translate
Killed by :
replaced return value with null for fr/sii/ogham/html/translator/InlineCssTranslator::translate → RUN_ERROR

88

1.1
Location : translate
Killed by :
replaced return value with null for fr/sii/ogham/html/translator/InlineCssTranslator::translate → RUN_ERROR

92

1.1
Location : getSourcePath
Killed by :
negated conditional → RUN_ERROR

93

1.1
Location : getSourcePath
Killed by :
replaced return value with null for fr/sii/ogham/html/translator/InlineCssTranslator::getSourcePath → NO_COVERAGE

95

1.1
Location : getSourcePath
Killed by :
replaced return value with null for fr/sii/ogham/html/translator/InlineCssTranslator::getSourcePath → RUN_ERROR

101

1.1
Location : load
Killed by :
removed call to fr/sii/ogham/html/translator/InlineCssTranslator::load → RUN_ERROR

103

1.1
Location : load
Killed by :
replaced return value with Collections.emptyList for fr/sii/ogham/html/translator/InlineCssTranslator::load → RUN_ERROR

118

1.1
Location : updateHtmlContent
Killed by :
negated conditional → RUN_ERROR

120

1.1
Location : updateHtmlContent
Killed by :
removed call to fr/sii/ogham/core/message/content/UpdatableStringContent::setStringContent → RUN_ERROR

125

1.1
Location : updateHtmlContent
Killed by :
replaced return value with null for fr/sii/ogham/html/translator/InlineCssTranslator::updateHtmlContent → RUN_ERROR

129

1.1
Location : clean
Killed by :
replaced return value with "" for fr/sii/ogham/html/translator/InlineCssTranslator::clean → RUN_ERROR

135

1.1
Location : toString
Killed by :
replaced return value with "" for fr/sii/ogham/html/translator/InlineCssTranslator::toString → RUN_ERROR

Active mutators

Tests examined


Report generated by PIT 1.13.1