blob: 6eba713d2a66e683c6681a3fdf09364e294ad307 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jps.maven.compiler;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.incremental.CompileContext;
import org.jetbrains.jps.incremental.messages.BuildMessage;
import org.jetbrains.jps.incremental.messages.CompilerMessage;
import org.jetbrains.jps.maven.model.impl.MavenModuleResourceConfiguration;
import org.jetbrains.jps.maven.model.impl.MavenProjectConfiguration;
import org.jetbrains.jps.maven.model.impl.ResourceRootConfiguration;
import org.jetbrains.jps.model.JpsEncodingConfigurationService;
import org.jetbrains.jps.model.JpsEncodingProjectConfiguration;
import org.jetbrains.jps.model.JpsProject;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author nik
*/
public class MavenResourceFileProcessor {
private static final int FILTERING_SIZE_LIMIT = 10 * 1024 * 1024 /*10 mb*/;
private static final String MAVEN_BUILD_TIMESTAMP_PROPERTY = "maven.build.timestamp";
private static final String MAVEN_BUILD_TIMESTAMP_FORMAT_PROPERTY = "maven.build.timestamp.format";
protected final Set<String> myFilteringExcludedExtensions;
protected final JpsEncodingProjectConfiguration myEncodingConfig;
protected final MavenProjectConfiguration myProjectConfig;
protected final MavenModuleResourceConfiguration myModuleConfiguration;
protected final Date myTimestamp;
private Map<String, String> myProperties;
private Pattern myDelimitersPattern;
public MavenResourceFileProcessor(MavenProjectConfiguration projectConfiguration, JpsProject project,
MavenModuleResourceConfiguration moduleConfiguration) {
myTimestamp = new Date();
myProjectConfig = projectConfiguration;
myEncodingConfig = JpsEncodingConfigurationService.getInstance().getEncodingConfiguration(project);
myModuleConfiguration = moduleConfiguration;
myFilteringExcludedExtensions = moduleConfiguration.getFilteringExcludedExtensions();
}
public void copyFile(File file, File targetFile, ResourceRootConfiguration rootConfiguration, CompileContext context,
FileFilter filteringFilter) throws IOException {
boolean shouldFilter = rootConfiguration.isFiltered && !myFilteringExcludedExtensions.contains(FileUtilRt.getExtension(file.getName()))
&& filteringFilter.accept(file);
if (shouldFilter && file.length() > FILTERING_SIZE_LIMIT) {
context.processMessage(new CompilerMessage("MavenResources", BuildMessage.Kind.WARNING,
"File is too big to be filtered. Most likely it is a binary file and should be excluded from filtering",
file.getPath()));
shouldFilter = false;
}
if (shouldFilter) {
copyWithFiltering(file, targetFile);
}
else {
FileUtil.copyContent(file, targetFile);
}
}
private void copyWithFiltering(File file, File outputFile) throws IOException {
final String encoding = myEncodingConfig != null? myEncodingConfig.getEncoding(file) : null;
PrintWriter writer;
try {
writer = encoding != null ? new PrintWriter(outputFile, encoding) : new PrintWriter(outputFile);
}
catch (FileNotFoundException e) {
FileUtil.createIfDoesntExist(outputFile);
writer = encoding != null ? new PrintWriter(outputFile, encoding) : new PrintWriter(outputFile);
}
try {
final byte[] bytes = FileUtil.loadFileBytes(file);
final String text = encoding != null? new String(bytes, encoding) : new String(bytes);
doFilterText(text, getDelimitersPattern(), getProperties(), null, writer);
}
finally {
writer.close();
}
}
private void doFilterText(String text, Pattern delimitersPattern,
@NotNull Map<String, String> additionalProperties,
@Nullable Map<String, String> resolvedPropertiesParam,
final Appendable out) throws IOException {
Map<String, String> resolvedProperties = resolvedPropertiesParam;
final Matcher matcher = delimitersPattern.matcher(text);
boolean hasEscapeString = !StringUtil.isEmpty(myModuleConfiguration.escapeString);
final int groupCount = matcher.groupCount();
int firstPropertyGroupIndex = hasEscapeString ? 3 : 0;
int last = 0;
while (matcher.find()) {
out.append(text, last, matcher.start());
last = matcher.end();
if (hasEscapeString) {
if (matcher.group(1) != null) {
out.append(myModuleConfiguration.escapeString).append(myModuleConfiguration.escapeString); // double escape string
continue;
}
else if (matcher.group(2) != null) {
out.append(matcher.group(3)); // escaped value
continue;
}
}
String propertyName = null;
for (int i = firstPropertyGroupIndex; i < groupCount; i++) {
propertyName = matcher.group(i + 1);
if (propertyName != null) {
break;
}
}
assert propertyName != null;
if (resolvedProperties == null) {
resolvedProperties = new HashMap<String, String>();
}
String propertyValue = resolvedProperties.get(propertyName);
if (propertyValue == null) {
if (resolvedProperties.containsKey(propertyName)) { // if cyclic property dependencies
out.append(matcher.group());
continue;
}
String resolved = myProjectConfig.resolveProperty(propertyName, myModuleConfiguration, additionalProperties);
if (resolved == null) {
out.append(matcher.group());
continue;
}
resolvedProperties.put(propertyName, null);
StringBuilder sb = new StringBuilder();
doFilterText(resolved, delimitersPattern, additionalProperties, resolvedProperties, sb);
propertyValue = sb.toString();
resolvedProperties.put(propertyName, propertyValue);
}
if (myModuleConfiguration.escapeWindowsPaths) {
MavenEscapeWindowsCharacterUtils.escapeWindowsPath(out, propertyValue);
}
else {
out.append(propertyValue);
}
}
out.append(text, last, text.length());
}
private Pattern getDelimitersPattern() {
Pattern pattern = myDelimitersPattern;
if (pattern == null) {
if (StringUtil.isEmpty(myModuleConfiguration.escapeString)) {
pattern = Pattern.compile(myModuleConfiguration.delimitersPattern);
}
else {
String quotedEscapeString = Pattern.quote(myModuleConfiguration.escapeString);
pattern = Pattern.compile("(" + quotedEscapeString + quotedEscapeString + ")|(?:(" + quotedEscapeString + ")?(" + myModuleConfiguration.delimitersPattern + "))");
}
myDelimitersPattern = pattern;
}
return pattern;
}
private Map<String, String> getProperties() {
Map<String, String> props = myProperties;
if (props == null) {
props = new HashMap<String, String>(myModuleConfiguration.properties);
String timestampFormat = props.get(MAVEN_BUILD_TIMESTAMP_FORMAT_PROPERTY);
if (timestampFormat == null) {
timestampFormat = "yyyyMMdd-HHmm"; // See ModelInterpolator.DEFAULT_BUILD_TIMESTAMP_FORMAT
}
props.put(MAVEN_BUILD_TIMESTAMP_PROPERTY, new SimpleDateFormat(timestampFormat).format(myTimestamp));
myProperties = props;
}
return props;
}
}